/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */

/*
  Part of the Processing project - http://processing.org

  Copyright (c) 2004-06 Ben Fry and Casey Reas
  Copyright (c) 2001-04 Massachusetts Institute of Technology

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General
  Public License along with this library; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA  02111-1307  USA
 */

package processing.core;

/**
 * Handles rendering of single (tesselated) triangles in 3D.
 * <P>
 * Written by sami www.sumea.com
 */
public class PTriangle implements PConstants
{
  static final int R_GOURAUD = 0x1;
  static final int R_TEXTURE8 = 0x2;
  static final int R_TEXTURE24 = 0x4;
  static final int R_TEXTURE32 = 0x8;
  static final int R_ALPHA = 0x10;

  private int[] m_pixels;
  private int[] m_texture;
  private int[] m_stencil;
  private float[] m_zbuffer;

  private int SCREEN_WIDTH;
  private int SCREEN_HEIGHT;
  //private int SCREEN_WIDTH1;
  //private int SCREEN_HEIGHT1;

  private int TEX_WIDTH;
  private int TEX_HEIGHT;
  private float F_TEX_WIDTH;
  private float F_TEX_HEIGHT;

  public  boolean INTERPOLATE_UV;
  public  boolean INTERPOLATE_RGB;
  public  boolean INTERPOLATE_ALPHA;

  // the power of 2 that tells how many pixels to interpolate
  // for between  exactly computed texture coordinates
  private static final int DEFAULT_INTERP_POWER = 3; 
  private static int TEX_INTERP_POWER = DEFAULT_INTERP_POWER;

  // Vertex coordinates
  private float[] x_array;
  private float[] y_array;
  private float[] z_array;

  private float[] camX;
  private float[] camY;
  private float[] camZ;

  // U,V coordinates
  private float[] u_array;
  private float[] v_array;

  // Vertex Intensity
  private float[] r_array;
  private float[] g_array;
  private float[] b_array;
  private float[] a_array;

  // vertex offsets
  private int o0;
  private int o1;
  private int o2;

  /* rgb & a */
  private float r0;
  private float r1;
  private float r2;
  private float g0;
  private float g1;
  private float g2;
  private float b0;
  private float b1;
  private float b2;
  private float a0;
  private float a1;
  private float a2;

  /* accurate texture uv coordinates */
  private float u0;
  private float u1;
  private float u2;
  private float v0;
  private float v1;
  private float v2;

  /* deltas */
  //private float dx0;
  //private float dx1;
  private float dx2;
  private float dy0;
  private float dy1;
  private float dy2;
  private float dz0;
  //private float dz1;
  private float dz2;

  /* texture deltas */
  private float du0;
  //private float du1;
  private float du2;
  private float dv0;
  //private float dv1;
  private float dv2;

  /* rgba deltas */
  private float dr0;
  //private float dr1;
  private float dr2;
  private float dg0;
  //private float dg1;
  private float dg2;
  private float db0;
  //private float db1;
  private float db2;
  private float da0;
  //private float da1;
  private float da2;

  /* */
  private float uleft;
  private float vleft;
  private float uleftadd;
  private float vleftadd;

  /* polyedge positions & adds */
  private float xleft;
  private float xrght;
  private float xadd1;
  private float xadd2;
  private float zleft;
  private float zleftadd;

  /* rgba positions & adds */
  private float rleft;
  private float gleft;
  private float bleft;
  private float aleft;
  private float rleftadd;
  private float gleftadd;
  private float bleftadd;
  private float aleftadd;

  /* other somewhat useful variables :) */
  private float dta;
  //private float dta2;
  private float temp;
  private float width;

  /* integer poly UV adds */
  private int iuadd;
  private int ivadd;
  private int iradd;
  private int igadd;
  private int ibadd;
  private int iaadd;
  private float izadd;

  /* fill color */
  private int m_fill;

  /* draw flags */
  public int  m_drawFlags;

  /* current poly number */
  private int m_index;

  /** */
  private PGraphics3D parent;

  private boolean noDepthTest;

  /** */
  private boolean   m_culling;

  /** */
  private boolean   m_singleRight;

  /** */
  private boolean   m_bilinear;
  
  
  //Vectors needed in accurate texture code
  //We store them as class members to avoid too much code duplication
  private float ax,ay,az;
  private float bx,by,bz;
  private float cx,cy,cz;
  private float nearPlaneWidth;
  private float nearPlaneHeight;
  private float nearPlaneDepth;
  private float xmult;
  private float ymult;
  private float newax,newbx,newcx; //optimization vars...not pretty, but save a couple mults per pixel
  private boolean firstSegment; //are we currently drawing the first piece of the triangle, or have we already done so?
  
  


  public PTriangle(PGraphics3D g) {
    //SCREEN_WIDTH = g.width;
    //SCREEN_HEIGHT = g.height;
    //SCREEN_WIDTH1 = SCREEN_WIDTH-1;
    //SCREEN_HEIGHT1 = SCREEN_HEIGHT-1;

    //m_pixels = g.pixels;
    //m_stencil = g.stencil;
    //m_zbuffer = g.zbuffer;

    x_array = new float[3];
    y_array = new float[3];
    z_array = new float[3];
    u_array = new float[3];
    v_array = new float[3];
    r_array = new float[3];
    g_array = new float[3];
    b_array = new float[3];
    a_array = new float[3];
    
	camX = new float[3];
	camY = new float[3];
	camZ = new float[3];

    this.parent = g;
    reset();
  }

  /**
   * Resets polygon attributes
   */
  public void reset() {
    // reset these in case PGraphics was resized

    SCREEN_WIDTH = parent.width;
    SCREEN_HEIGHT = parent.height;
    //SCREEN_WIDTH1 = SCREEN_WIDTH-1;
    //SCREEN_HEIGHT1 = SCREEN_HEIGHT-1;

    m_pixels = parent.pixels;
    m_stencil = parent.stencil;
    m_zbuffer = parent.zbuffer;

    noDepthTest = parent.hints[DISABLE_DEPTH_TEST];

    // other things to reset

    INTERPOLATE_UV = false;
    INTERPOLATE_RGB = false;
    INTERPOLATE_ALPHA = false;
    //m_tImage = null;
    m_texture = null;
    m_drawFlags = 0;
  }

  /**
   * Sets backface culling on/off
   */
  public void setCulling(boolean tf) {
    m_culling = tf;
  }


  /**
   * Sets vertex coordinates for the triangle
   */
  public void setVertices(float x0, float y0, float z0,
                          float x1, float y1, float z1,
                          float x2, float y2, float z2) {
    x_array[0] = x0;
    x_array[1] = x1;
    x_array[2] = x2;

    y_array[0] = y0;
    y_array[1] = y1;
    y_array[2] = y2;

    z_array[0] = z0;
    z_array[1] = z1;
    z_array[2] = z2;
  }

  /**
   * Pass camera-space coordinates for the triangle (needed to render if ENABLE_ACCURATE_TEXTURES is hinted).
   */
  public void setCamVertices(float x0, float y0, float z0,
                          float x1, float y1, float z1,
                          float x2, float y2, float z2) {
	//Generally this will not need to be called manually, currently called if hints[ENABLE_ACCURATE_TEXTURES]
	//from PGraphics3D.render_triangles()

    camX[0] = x0;
    camX[1] = x1;
    camX[2] = x2;

    camY[0] = y0;
    camY[1] = y1;
    camY[2] = y2;

    camZ[0] = z0;
    camZ[1] = z1;
    camZ[2] = z2;
  }

  /**
   * Sets the UV coordinates of the texture
   */
  public void setUV(float u0, float v0,
                    float u1, float v1,
                    float u2, float v2) {
    // sets & scales uv texture coordinates to center of the pixel
    u_array[0] = (u0 * F_TEX_WIDTH + 0.5f) * 65536f;
    u_array[1] = (u1 * F_TEX_WIDTH + 0.5f) * 65536f;
    u_array[2] = (u2 * F_TEX_WIDTH + 0.5f) * 65536f;
    v_array[0] = (v0 * F_TEX_HEIGHT + 0.5f) * 65536f;
    v_array[1] = (v1 * F_TEX_HEIGHT + 0.5f) * 65536f;
    v_array[2] = (v2 * F_TEX_HEIGHT + 0.5f) * 65536f;
  }

  /**
   * Sets vertex intensities in 0xRRGGBBAA format
   */
  public void setIntensities( float r0, float g0, float b0, float a0,
                float r1, float g1, float b1, float a1,
                float r2, float g2, float b2, float a2) {
    // Check if we need alpha or not?
    if ((a0 != 1.0f) || (a1 != 1.0f) || (a2 != 1.0f)) {
      INTERPOLATE_ALPHA = true;
      a_array[0] = (a0 * 253f + 1.0f) * 65536f;
      a_array[1] = (a1 * 253f + 1.0f) * 65536f;
      a_array[2] = (a2 * 253f + 1.0f) * 65536f;
      m_drawFlags|=R_ALPHA;
    } else {
      INTERPOLATE_ALPHA = false;
      m_drawFlags&=~R_ALPHA;
    }

    // Check if we need to interpolate the intensity values
    if ((r0 != r1) || (r1 != r2)) {
      INTERPOLATE_RGB = true;
      m_drawFlags|=R_GOURAUD;
    } else if ((g0 != g1) || (g1 != g2)) {
      INTERPOLATE_RGB = true;
      m_drawFlags|=R_GOURAUD;
    } else if ((b0 != b1) || (b1 != b2)) {
      INTERPOLATE_RGB = true;
      m_drawFlags|=R_GOURAUD;
    } else {
      //m_fill = parent.filli;
      m_drawFlags&=~R_GOURAUD;
    }

    // push values to arrays.. some extra scaling is added
    // to prevent possible color "overflood" due to rounding errors
    r_array[0] = (r0 * 253f + 1.0f) * 65536f;
    r_array[1] = (r1 * 253f + 1.0f) * 65536f;
    r_array[2] = (r2 * 253f + 1.0f) * 65536f;

    g_array[0] = (g0 * 253f + 1.0f) * 65536f;
    g_array[1] = (g1 * 253f + 1.0f) * 65536f;
    g_array[2] = (g2 * 253f + 1.0f) * 65536f;

    b_array[0] = (b0 * 253f + 1.0f) * 65536f;
    b_array[1] = (b1 * 253f + 1.0f) * 65536f;
    b_array[2] = (b2 * 253f + 1.0f) * 65536f;

    // for plain triangles
    m_fill = ((int)(255*r0) << 16) | ((int)(255*g0) << 8) | (int)(255*b0);
 }


  /**
   * Sets texture image used for the polygon
   */
  public void setTexture(PImage image) {
    //m_tImage = image;
    m_texture = image.pixels;
    TEX_WIDTH = image.width;
    TEX_HEIGHT = image.height;
    F_TEX_WIDTH = TEX_WIDTH-1;
    F_TEX_HEIGHT = TEX_HEIGHT-1;
    INTERPOLATE_UV = true;

    if (image.format == ARGB) {
      m_drawFlags|=R_TEXTURE32;
    } else if (image.format == RGB) {
      m_drawFlags|=R_TEXTURE24;
    } else if (image.format == ALPHA) {
      m_drawFlags|=R_TEXTURE8;
    }

    //if (parent.hints[SMOOTH_IMAGES]) {
    /*
    if (parent.smooth) {
      m_bilinear = true;
    } else {
      m_bilinear = false;
    }
    */
    m_bilinear = true;
  }

  /**
   *
   */
  public void setUV(float[] u, float[] v) {
    if (m_bilinear) {
      // sets & scales uv texture coordinates to edges of pixels
      u_array[0] = (u[0] * F_TEX_WIDTH) * 65500f;
      u_array[1] = (u[1] * F_TEX_WIDTH) * 65500f;
      u_array[2] = (u[2] * F_TEX_WIDTH) * 65500f;
      v_array[0] = (v[0] * F_TEX_HEIGHT) * 65500f;
      v_array[1] = (v[1] * F_TEX_HEIGHT) * 65500f;
      v_array[2] = (v[2] * F_TEX_HEIGHT) * 65500f;
    } else {
      // sets & scales uv texture coordinates to center of the pixel
      u_array[0] = (u[0] * TEX_WIDTH) * 65500f;
      u_array[1] = (u[1] * TEX_WIDTH) * 65500f;
      u_array[2] = (u[2] * TEX_WIDTH) * 65500f;
      v_array[0] = (v[0] * TEX_HEIGHT) * 65500f;
      v_array[1] = (v[1] * TEX_HEIGHT) * 65500f;
      v_array[2] = (v[2] * TEX_HEIGHT) * 65500f;
    }
  }

  public void setIndex(int index) {
    m_index = index;
  }

  /**
   * Renders the polygon
   */
  public void render() {
    // removed. done in PGraphics [rocha]
    // increase polygon offset
    //m_index = (m_index + 1) & 0xFFFFFFF;

    // draw the polygon
    draw();

    // removed. replaced by external antialiasing [rocha]
    // smooth edges?
    //if (parent.smooth )
    //{
     // drawline_blender(x_array[0], y_array[0], x_array[1], y_array[1]);
     // drawline_blender(x_array[1], y_array[1], x_array[2], y_array[2]);
     // drawline_blender(x_array[2], y_array[2], x_array[0], y_array[0]);
    //}
  }

  private void draw() {
    // y-coordinates
    float x0;
    float x1;
    float x2;

    //
    float z0;
    float z1;
    float z2;

    //
    float y0 = y_array[0];
    float y1 = y_array[1];
    float y2 = y_array[2];

    // For accurate texture interpolation, need to mark whether 
    // we've already pre-calculated for the triangle
	firstSegment = true; 

    // do backface culling?
    if (m_culling) {
      x0 = x_array[0];
      if ((x_array[2]-x0)*(y1-y0) < (x_array[1]-x0)*(y2-y0))
        return;
    }

    /* get vertex order from top -> down */
    if (y0<y1) {
      if (y2<y1) {
        if (y2<y0)  // 2,0,1
        {
          o0=2;
          o1=0;
          o2=1;
        }
        else  // 0,2,1
        {
          o0=0;
          o1=2;
          o2=1;
        }
      }
      else  // 0,1,2
      {
        o0=0;
        o1=1;
        o2=2;
      }
    } else {
      if (y2>y1) {
        if (y2<y0)  // 1,2,0
        {
          o0=1;
          o1=2;
          o2=0;
        }
        else  // 1,0,2
        {
          o0=1;
          o1=0;
          o2=2;
        }
      }
      else  // 2,1,0
      {
        o0=2;
        o1=1;
        o2=0;
      }
    }

    /**
      * o0 = "top" vertex offset
      * o1 = "mid" vertex offset
      * o2 = "bot" vertex offset
     */

    y0 = y_array[o0];
    int yi0 = (int) (y0 + PIXEL_CENTER);
    if (yi0 > SCREEN_HEIGHT) {
      return;
    } else if (yi0 < 0) {
      yi0 = 0;
    }

    y2 = y_array[o2];
    int yi2 = (int) (y2 + PIXEL_CENTER);
    if (yi2 < 0) {
      return;
    } else if (yi2 > SCREEN_HEIGHT) {
      yi2 = SCREEN_HEIGHT;
    }

    // Does the poly actually cross a scanline?
    if (yi2 > yi0) {
      x0 = x_array[o0];
      x1 = x_array[o1];
      x2 = x_array[o2];

      // get mid Y and clip it
      y1 = y_array[o1];
      int yi1 = (int) (y1 + PIXEL_CENTER);
      if (yi1 < 0)
        yi1 = 0;
      if (yi1 > SCREEN_HEIGHT)
        yi1 = SCREEN_HEIGHT;

      // calculate deltas etc.
      dx2 = x2 - x0;
      dy0 = y1 - y0;
      dy2 = y2 - y0;
      xadd2 = dx2 / dy2;          // xadd for "single" edge
      temp = dy0 / dy2;
      width = temp * dx2 + x0 - x1;

      // calculate alpha blend interpolation
      if (INTERPOLATE_ALPHA) {
        a0 = a_array[o0];
        a1 = a_array[o1];
        a2 = a_array[o2];
        da0 = a1-a0;
        da2 = a2-a0;
        iaadd = (int) ((temp * da2 - da0) / width); // alpha add
      }

      // calculate intensity interpolation
      if (INTERPOLATE_RGB) {
        r0 = r_array[o0];
        r1 = r_array[o1];
        r2 = r_array[o2];

        g0 = g_array[o0];
        g1 = g_array[o1];
        g2 = g_array[o2];

        b0 = b_array[o0];
        b1 = b_array[o1];
        b2 = b_array[o2];

        dr0 = r1-r0;
        dg0 = g1-g0;
        db0 = b1-b0;

        dr2 = r2-r0;
        dg2 = g2-g0;
        db2 = b2-b0;

        iradd = (int) ((temp * dr2 - dr0) / width); // r add
        igadd = (int) ((temp * dg2 - dg0) / width); // g add
        ibadd = (int) ((temp * db2 - db0) / width); // b add
      }

      // calculate UV interpolation
      if (INTERPOLATE_UV) {
        u0 = u_array[o0];
        u1 = u_array[o1];
        u2 = u_array[o2];
        v0 = v_array[o0];
        v1 = v_array[o1];
        v2 = v_array[o2];
        du0 = u1-u0;
        dv0 = v1-v0;
        du2 = u2-u0;
        dv2 = v2-v0;
        iuadd = (int) ((temp * du2 - du0) / width); // u add
        ivadd = (int) ((temp * dv2 - dv0) / width); // v add
      }

      z0 = z_array[o0];
      z1 = z_array[o1];
      z2 = z_array[o2];
      dz0 = z1-z0;
      dz2 = z2-z0;
      izadd = (temp * dz2 - dz0) / width;

      // draw the upper poly segment if it's visible
      if (yi1 > yi0) {
        dta = (yi0 + PIXEL_CENTER) - y0;
        xadd1 = (x1 - x0) / dy0;

        // we can determine which side is "single" side by comparing left/right edge adds
        if (xadd2 > xadd1) {
          xleft = x0 + dta * xadd1;
          xrght = x0 + dta * xadd2;
          zleftadd = dz0 / dy0;
          zleft = dta*zleftadd+z0;

          //
          if (INTERPOLATE_UV) {
            uleftadd = du0 / dy0;
            vleftadd = dv0 / dy0;
            uleft = dta*uleftadd+u0;
            vleft = dta*vleftadd+v0;
          }

          //
          if (INTERPOLATE_RGB) {
            rleftadd = dr0 / dy0;
            gleftadd = dg0 / dy0;
            bleftadd = db0 / dy0;
            rleft = dta*rleftadd+r0;
            gleft = dta*gleftadd+g0;
            bleft = dta*bleftadd+b0;
          }

          //
          if (INTERPOLATE_ALPHA) {
            aleftadd = da0 / dy0;
            aleft = dta*aleftadd+a0;

            if (m_drawFlags == R_ALPHA) {
              drawsegment_plain_alpha(xadd1,xadd2, yi0,yi1);
            } else if (m_drawFlags == (R_GOURAUD + R_ALPHA)) {
              drawsegment_gouraud_alpha(xadd1,xadd2, yi0,yi1);
            } else if (m_drawFlags == (R_TEXTURE8 + R_ALPHA)) {
              drawsegment_texture8_alpha(xadd1,xadd2, yi0,yi1);
            } else if (m_drawFlags == (R_TEXTURE24 + R_ALPHA)) {
              drawsegment_texture24_alpha(xadd1,xadd2, yi0,yi1);
            } else if (m_drawFlags == (R_TEXTURE32 + R_ALPHA)) {
              drawsegment_texture32_alpha(xadd1,xadd2, yi0,yi1);
            } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8 + R_ALPHA)) {
              drawsegment_gouraud_texture8_alpha(xadd1,xadd2, yi0,yi1);
            } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24 + R_ALPHA)) {
              drawsegment_gouraud_texture24_alpha(xadd1,xadd2, yi0,yi1);
            } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32 + R_ALPHA)) {
              drawsegment_gouraud_texture32_alpha(xadd1,xadd2, yi0,yi1);
            }
          } else {
            if (m_drawFlags == 0) {
              drawsegment_plain(xadd1,xadd2, yi0,yi1);
            } else if (m_drawFlags == R_GOURAUD) {
              drawsegment_gouraud(xadd1,xadd2, yi0,yi1);
            } else if (m_drawFlags == R_TEXTURE8) {
              drawsegment_texture8(xadd1,xadd2, yi0,yi1);
            } else if (m_drawFlags == R_TEXTURE24) {
              drawsegment_texture24(xadd1,xadd2, yi0,yi1);
            } else if (m_drawFlags == R_TEXTURE32) {
              drawsegment_texture32(xadd1,xadd2, yi0,yi1);
            } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8)) {
              drawsegment_gouraud_texture8(xadd1,xadd2, yi0,yi1);
            } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24)) {
              drawsegment_gouraud_texture24(xadd1,xadd2, yi0,yi1);
            } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32)) {
              drawsegment_gouraud_texture32(xadd1,xadd2, yi0,yi1);
            }
          }
          m_singleRight = true;
        } else {
          xleft = x0 + dta * xadd2;
          xrght = x0 + dta * xadd1;
          zleftadd = dz2 / dy2;
          zleft = dta*zleftadd+z0;
          //
          if (INTERPOLATE_UV) {
            uleftadd = du2 / dy2;
            vleftadd = dv2 / dy2;
            uleft = dta*uleftadd+u0;
            vleft = dta*vleftadd+v0;
          }

          //
          if (INTERPOLATE_RGB) {
            rleftadd = dr2 / dy2;
            gleftadd = dg2 / dy2;
            bleftadd = db2 / dy2;
            rleft = dta*rleftadd+r0;
            gleft = dta*gleftadd+g0;
            bleft = dta*bleftadd+b0;
          }


          if (INTERPOLATE_ALPHA) {
            aleftadd = da2 / dy2;
            aleft = dta*aleftadd+a0;

            if (m_drawFlags == R_ALPHA) {
              drawsegment_plain_alpha(xadd2, xadd1, yi0,yi1);
            } else if (m_drawFlags == (R_GOURAUD + R_ALPHA)) {
              drawsegment_gouraud_alpha(xadd2, xadd1, yi0,yi1);
            } else if (m_drawFlags == (R_TEXTURE8 + R_ALPHA)) {
              drawsegment_texture8_alpha(xadd2, xadd1, yi0,yi1);
            } else if (m_drawFlags == (R_TEXTURE24 + R_ALPHA)) {
              drawsegment_texture24_alpha(xadd2, xadd1, yi0,yi1);
            } else if (m_drawFlags == (R_TEXTURE32 + R_ALPHA)) {
              drawsegment_texture32_alpha(xadd2, xadd1, yi0,yi1);
            } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8 + R_ALPHA)) {
              drawsegment_gouraud_texture8_alpha(xadd2, xadd1, yi0,yi1);
            } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24 + R_ALPHA)) {
              drawsegment_gouraud_texture24_alpha(xadd2, xadd1, yi0,yi1);
            } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32 + R_ALPHA)) {
              drawsegment_gouraud_texture32_alpha(xadd2, xadd1, yi0,yi1);
            }
          } else {
            if (m_drawFlags == 0) {
              drawsegment_plain(xadd2, xadd1, yi0,yi1);
            } else if (m_drawFlags == R_GOURAUD) {
              drawsegment_gouraud(xadd2, xadd1, yi0,yi1);
            } else if (m_drawFlags == R_TEXTURE8) {
              drawsegment_texture8(xadd2, xadd1, yi0,yi1);
            } else if (m_drawFlags == R_TEXTURE24) {
              drawsegment_texture24(xadd2, xadd1, yi0,yi1);
            } else if (m_drawFlags == R_TEXTURE32) {
              drawsegment_texture32(xadd2, xadd1, yi0,yi1);
            } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8)) {
              drawsegment_gouraud_texture8(xadd2, xadd1, yi0,yi1);
            } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24)) {
              drawsegment_gouraud_texture24(xadd2, xadd1, yi0,yi1);
            } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32)) {
              drawsegment_gouraud_texture32(xadd2, xadd1, yi0,yi1);
            }
          }
          m_singleRight = false;
        }

        // if bottom segment height is zero, return
        if (yi2 == yi1)
          return;

        // calculate xadd 1
        dy1 = y2 - y1;
        xadd1 = (x2 - x1) / dy1;
      } else {
        // top seg height was zero, calculate & clip single edge X
        dy1 = y2 - y1;
        xadd1 = (x2 - x1) / dy1;

        // which edge is left?
        if (xadd2 < xadd1) {
          xrght = ((yi1 + PIXEL_CENTER) - y0) * xadd2 + x0;
          m_singleRight = true;
        } else {
          dta = (yi1 + PIXEL_CENTER) - y0;
          xleft = dta * xadd2 + x0;
          zleftadd = dz2 / dy2;
          zleft = dta * zleftadd + z0;

          if (INTERPOLATE_UV) {
            uleftadd = du2 / dy2;
            vleftadd = dv2 / dy2;
            uleft = dta * uleftadd + u0;
            vleft = dta * vleftadd + v0;
          }

          if (INTERPOLATE_RGB) {
            rleftadd = dr2 / dy2;
            gleftadd = dg2 / dy2;
            bleftadd = db2 / dy2;
            rleft = dta * rleftadd + r0;
            gleft = dta * gleftadd + g0;
            bleft = dta * bleftadd + b0;
          }

          //
          if (INTERPOLATE_ALPHA) {
            aleftadd = da2 / dy2;
            aleft = dta * aleftadd + a0;
          }
          m_singleRight = false;
        }
      }

      // draw the lower segment
      if (m_singleRight) {
        dta = (yi1 + PIXEL_CENTER) - y1;
        xleft = dta * xadd1 + x1;
        zleftadd = (z2 - z1) / dy1;
        zleft = dta * zleftadd + z1;

        if (INTERPOLATE_UV) {
          uleftadd = (u2 - u1) / dy1;
          vleftadd = (v2 - v1) / dy1;
          uleft = dta * uleftadd + u1;
          vleft = dta * vleftadd + v1;
        }

        if (INTERPOLATE_RGB) {
          rleftadd = (r2 - r1) / dy1;
          gleftadd = (g2 - g1) / dy1;
          bleftadd = (b2 - b1) / dy1;
          rleft = dta * rleftadd + r1;
          gleft = dta * gleftadd + g1;
          bleft = dta * bleftadd + b1;
        }

        if (INTERPOLATE_ALPHA) {
          aleftadd = (a2 - a1) / dy1;
          aleft = dta * aleftadd + a1;

          if (m_drawFlags == R_ALPHA) {
            drawsegment_plain_alpha(xadd1, xadd2, yi1,yi2);
          } else if (m_drawFlags == (R_GOURAUD + R_ALPHA)) {
            drawsegment_gouraud_alpha(xadd1, xadd2, yi1,yi2);
          } else if (m_drawFlags == (R_TEXTURE8 + R_ALPHA)) {
            drawsegment_texture8_alpha(xadd1, xadd2, yi1,yi2);
          } else if (m_drawFlags == (R_TEXTURE24 + R_ALPHA)) {
            drawsegment_texture24_alpha(xadd1, xadd2, yi1,yi2);
          } else if (m_drawFlags == (R_TEXTURE32 + R_ALPHA)) {
            drawsegment_texture32_alpha(xadd1, xadd2, yi1,yi2);
          } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8 + R_ALPHA)) {
            drawsegment_gouraud_texture8_alpha(xadd1, xadd2, yi1,yi2);
          } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24 + R_ALPHA)) {
            drawsegment_gouraud_texture24_alpha(xadd1, xadd2, yi1,yi2);
          } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32 + R_ALPHA)) {
            drawsegment_gouraud_texture32_alpha(xadd1, xadd2, yi1,yi2);
          }
        } else {
          if (m_drawFlags == 0) {
            drawsegment_plain(xadd1, xadd2, yi1,yi2);
          } else if (m_drawFlags == R_GOURAUD) {
            drawsegment_gouraud(xadd1, xadd2, yi1,yi2);
          } else if (m_drawFlags == R_TEXTURE8) {
            drawsegment_texture8(xadd1, xadd2, yi1,yi2);
          } else if (m_drawFlags == R_TEXTURE24) {
            drawsegment_texture24(xadd1, xadd2, yi1,yi2);
          } else if (m_drawFlags == R_TEXTURE32) {
            drawsegment_texture32(xadd1, xadd2, yi1,yi2);
          } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8)) {
            drawsegment_gouraud_texture8(xadd1, xadd2, yi1,yi2);
          } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24)) {
            drawsegment_gouraud_texture24(xadd1, xadd2, yi1,yi2);
          } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32)) {
            drawsegment_gouraud_texture32(xadd1, xadd2, yi1,yi2);
          }
        }
      } else {
        xrght = ((yi1 + PIXEL_CENTER)- y1) * xadd1 + x1;

        if (INTERPOLATE_ALPHA) {
          if (m_drawFlags == R_ALPHA) {
            drawsegment_plain_alpha(xadd2, xadd1, yi1,yi2);
          } else if (m_drawFlags == (R_GOURAUD + R_ALPHA)) {
            drawsegment_gouraud_alpha(xadd2, xadd1, yi1,yi2);
          } else if (m_drawFlags == (R_TEXTURE8 + R_ALPHA)) {
            drawsegment_texture8_alpha(xadd2, xadd1, yi1,yi2);
          } else if (m_drawFlags == (R_TEXTURE24 + R_ALPHA)) {
            drawsegment_texture24_alpha(xadd2, xadd1, yi1,yi2);
          } else if (m_drawFlags == (R_TEXTURE32 + R_ALPHA)) {
            drawsegment_texture32_alpha(xadd2, xadd1, yi1,yi2);
          } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8 + R_ALPHA)) {
            drawsegment_gouraud_texture8_alpha(xadd2, xadd1, yi1,yi2);
          } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24 + R_ALPHA)) {
            drawsegment_gouraud_texture24_alpha(xadd2, xadd1, yi1,yi2);
          } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32 + R_ALPHA)) {
            drawsegment_gouraud_texture32_alpha(xadd2, xadd1, yi1,yi2);
          }
        } else {
          if (m_drawFlags == 0) {
            drawsegment_plain(xadd2, xadd1, yi1,yi2);
          } else if (m_drawFlags == R_GOURAUD) {
            drawsegment_gouraud(xadd2, xadd1, yi1,yi2);
          } else if (m_drawFlags == R_TEXTURE8) {
            drawsegment_texture8(xadd2, xadd1, yi1,yi2);
          } else if (m_drawFlags == R_TEXTURE24) {
            drawsegment_texture24(xadd2, xadd1, yi1,yi2);
          } else if (m_drawFlags == R_TEXTURE32) {
            drawsegment_texture32(xadd2, xadd1, yi1,yi2);
          } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8)) {
            drawsegment_gouraud_texture8(xadd2, xadd1, yi1,yi2);
          } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24)) {
            drawsegment_gouraud_texture24(xadd2, xadd1, yi1,yi2);
          } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32)) {
            drawsegment_gouraud_texture32(xadd2, xadd1, yi1,yi2);
          }
        }
      }
          }
        }


  /*
    Accurate texturing code by ewjordan@gmail.com, April 14, 2007
    The getColorFromTexture() function should be inlined and optimized so that most of the heavy lifting
    happens outside the per-pixel loop.  The unoptimized generic algorithm looks like this (unless noted,
	all of these variables are vectors, so the actual code will look messier):
	
	p = camera space vector where u == 0, v == 0;
	m = vector from p to location where u == TEX_WIDTH;
	n = vector from p to location where v == TEX_HEIGHT;

	A = p cross n;
	B = m cross p;
	C = n cross m;
    A *= texture.width;
    B *= texture.height;
	
	for (scanlines in triangle){
      float a = S * A;
      float b = S * B;
      float c = S * C;
      for (pixels in scanline){
        int u = a/c;
        int v = b/c;
        color = texture[v * texture.width + u];
        a += A.x;
        b += B.x;
        c += C.x;
      }
    }
	
	We don't use this exact algorithm here, however, because of the extra overhead from the divides.
	Instead we compute the exact u and v (labelled iu and iv in the code) at the start of each scanline
	and we perform a linear interpolation for every linearInterpLength = 1 << TEX_INTERP_POWER pixels.
	This means that we only perform the true calculation once in a while, and the rest of the time
	the algorithm functions exactly as in the fast inaccurate case, at least in theory.  In practice,
	even if we set linearInterpLength very high we still incur some speed penalty due to the preprocessing
	that must take place per-scanline.  A similar method could be applied per scanline to avoid this, but
	it would only be worthwhile in the case that we never compute more than one exact calculation per
	scanline.  If we want something like this, however, it would be best to create another mode of calculation
	called "constant-z" interpolation, which could be used for things like floors and ceilings where the
	distance from the camera plane never changes over the course of a scanline.  We could also add the
	vertical analogue for drawing vertical walls.  In any case, these are not critical as the current
	algorithm runs fairly well, perhaps ~10% slower than the default perspective-less one.
	
  */
  
 /**
   * Solve for camera space coordinates of critical texture points and set up per-triangle variables for accurate texturing
   */
  private boolean precomputeAccurateTexturing(){
    //Sets all class variables relevant to accurate texture computation
	//Should be called once per triangle - checks firstSegment to see if we've already called
	
  	float myFact = 65500.0f; //rescale u/v_array values when inverting matrix and performing other calcs
	float myFact2 = 65500.0f;

	//Matrix inversion to find affine transform between (u,v,(1)) -> (x,y,z)
	
	//OPTIMIZE: There should be a way to avoid the inversion here, which is
	//quite expensive (~150 mults).  Also it might crap out due to loss of precision depending
	//on the scaling of the u/v_arrays.  Nothing clever currently happens if the inversion
	//fails, since this means the transformation is degenerate - we just pass false back to
	//the caller and let it handle the situation. [There is no good solution to this
	//case from within this function, since the way the calculation proceeds presumes a non-
	//degenerate transformation matrix between camera space and uv space]
	
	//Obvious optimization: if the vertices are actually at the appropriate texture coordinates
	//(e.g. (0,0), (TEX_WIDTH,0), and (0,TEX_HEIGHT)) then we can immediately return the
	//right solution without the inversion. This is fairly common, so could speed up
	//many cases of drawing.  [not implemented]
	
	//Furthermore, we could cache the p,resultT0,result0T vectors in the triangle's
	//basis, since we could then do a look-up and generate the resulting coordinates very simply.
	//This would include the above optimization as a special case - we could pre-populate the
	//cache with special cases like that and dynamically add more.  The idea here is
	//that most people simply paste textures onto triangles and move the triangles from
	//frame to frame, so any per-triangle-per-frame code is likely wasted effort.
    //[not implemented]
	
	//Note: o0, o1, and o2 vary depending on view angle to triangle, but p, n, and m should not depend on ordering differences

	if(firstSegment){
		
		PMatrix myMatrix = new PMatrix( u_array[o0]/myFact, v_array[o0]/myFact2, 1, 0,
										u_array[o1]/myFact, v_array[o1]/myFact2, 1, 0,
										u_array[o2]/myFact, v_array[o2]/myFact2, 1, 0,
										0,           0,           0, 1);
		myMatrix = myMatrix.invert(); //A 3x3 inversion would be more efficient here, given that the fourth r/c are unity
		if (myMatrix == null) {return false;} //if the matrix inversion had trouble, let the caller know
		float m00, m01, m02, m10, m11, m12, m20, m21, m22;
		m00 = myMatrix.m00*camX[o0]+myMatrix.m01*camX[o1]+myMatrix.m02*camX[o2];
		m01 = myMatrix.m10*camX[o0]+myMatrix.m11*camX[o1]+myMatrix.m12*camX[o2];
		m02 = myMatrix.m20*camX[o0]+myMatrix.m21*camX[o1]+myMatrix.m22*camX[o2];
		m10 = myMatrix.m00*camY[o0]+myMatrix.m01*camY[o1]+myMatrix.m02*camY[o2];
		m11 = myMatrix.m10*camY[o0]+myMatrix.m11*camY[o1]+myMatrix.m12*camY[o2];
		m12 = myMatrix.m20*camY[o0]+myMatrix.m21*camY[o1]+myMatrix.m22*camY[o2];
		m20 = -(myMatrix.m00*camZ[o0]+myMatrix.m01*camZ[o1]+myMatrix.m02*camZ[o2]);
		m21 = -(myMatrix.m10*camZ[o0]+myMatrix.m11*camZ[o1]+myMatrix.m12*camZ[o2]);
		m22 = -(myMatrix.m20*camZ[o0]+myMatrix.m21*camZ[o1]+myMatrix.m22*camZ[o2]);
		
		float px = m02;
		float py = m12;
		float pz = m22;
		float resultT0x = m00*TEX_WIDTH+m02; //Bugfix: possibly we should use F_TEX_WIDTH/HEIGHT instead? Seems to read off end of array in that case, though...
		float resultT0y = m10*TEX_WIDTH+m12;
		float resultT0z = m20*TEX_WIDTH+m22;
		float result0Tx = m01*TEX_HEIGHT+m02;
		float result0Ty = m11*TEX_HEIGHT+m12;
		float result0Tz = m21*TEX_HEIGHT+m22;	  
		float mx = resultT0x-m02;
		float my = resultT0y-m12;
		float mz = resultT0z-m22;
		float nx = result0Tx-m02;
		float ny = result0Ty-m12;
		float nz = result0Tz-m22;
		
		//avec = p x n
		ax = (py*nz-pz*ny)*TEX_WIDTH; //F_TEX_WIDTH/HEIGHT?
		ay = (pz*nx-px*nz)*TEX_WIDTH;
		az = (px*ny-py*nx)*TEX_WIDTH;
		//bvec = m x p
		bx = (my*pz-mz*py)*TEX_HEIGHT;
		by = (mz*px-mx*pz)*TEX_HEIGHT;
		bz = (mx*py-my*px)*TEX_HEIGHT;
		//cvec = n x m
		cx = ny*mz-nz*my;
		cy = nz*mx-nx*mz;
		cz = nx*my-ny*mx;
	}
	
    nearPlaneWidth = parent.rightScreen-parent.leftScreen;
	nearPlaneHeight = parent.topScreen-parent.bottomScreen;
	nearPlaneDepth = parent.nearPlane;
	xmult = nearPlaneWidth / SCREEN_WIDTH; //one pixel width in nearPlane coordinates
	ymult = nearPlaneHeight / SCREEN_HEIGHT;
    newax = ax*xmult;//Extra scalings to map screen plane units to pixel units
    newbx = bx*xmult;
    newcx = cx*xmult;
	return true;
  }

 /**
   * Set the power of two used for linear interpolation of texture coordinates.
   * A true texture coordinate is computed every 2^pwr pixels along a scanline.
   */
  static public void setInterpPower(int pwr){
    //Currently must be invoked from P5 as PTriangle.setInterpPower(...)
    TEX_INTERP_POWER = pwr;
  }



  /**
   * Plain color
   */
  private void drawsegment_plain
  (
    float leftadd,
    float rghtadd,
    int ytop,
    int ybottom
  ) {
    ytop*=SCREEN_WIDTH;
    ybottom*=SCREEN_WIDTH;
    int f = m_fill;
    int p = m_index;

    while (ytop < ybottom) {
      int xstart = (int) (xleft + PIXEL_CENTER);
      if (xstart < 0)
        xstart = 0;

      int xend = (int) (xrght + PIXEL_CENTER);
      if (xend > SCREEN_WIDTH)
        xend = SCREEN_WIDTH;

      float xdiff = (xstart + PIXEL_CENTER) - xleft;
      float iz = izadd * xdiff + zleft;
      xstart+=ytop;
      xend+=ytop;

      for ( ; xstart < xend; xstart++ ) {
        if (noDepthTest || (iz <= m_zbuffer[xstart])) {
          m_zbuffer[xstart] = iz;
          m_pixels[xstart] = f;
          m_stencil[xstart] = p;
        }
        iz+=izadd;
      }

      ytop+=SCREEN_WIDTH;
      xleft+=leftadd;
      xrght+=rghtadd;
      zleft+=zleftadd;
    }
  }

  /**
   * Plain color, interpolated alpha
   */
  private void drawsegment_plain_alpha
  (
    float leftadd,
    float rghtadd,
    int ytop,
    int ybottom
  ) {
    ytop*=SCREEN_WIDTH;
    ybottom*=SCREEN_WIDTH;

    int pr = m_fill & 0xFF0000;
    int pg = m_fill & 0xFF00;
    int pb = m_fill & 0xFF;

    int p = m_index;
    float iaf = iaadd;

    while (ytop < ybottom) {
      int xstart = (int) (xleft + PIXEL_CENTER);
      if (xstart < 0)
        xstart = 0;

      int xend = (int) (xrght + PIXEL_CENTER);
      if (xend > SCREEN_WIDTH)
        xend = SCREEN_WIDTH;

      float xdiff = (xstart + PIXEL_CENTER) - xleft;
      float iz = izadd * xdiff + zleft;
      int ia = (int) (iaf * xdiff + aleft);
      xstart+=ytop;
      xend+=ytop;

      for ( ; xstart < xend; xstart++ ) {
        if (noDepthTest || (iz <= m_zbuffer[xstart])) {
          //m_zbuffer[xstart] = iz;

          int alpha = ia >> 16;
          int mr0 = m_pixels[xstart];
          int mg0 = mr0 & 0xFF00;
          int mb0 = mr0 & 0xFF;
          mr0 &= 0xFF0000;

          mr0 = mr0 + (((pr - mr0) * alpha) >> 8);
          mg0 = mg0 + (((pg - mg0) * alpha) >> 8);
          mb0 = mb0 + (((pb - mb0) * alpha) >> 8);
          m_pixels[xstart] = (mr0 & 0xFF0000) | (mg0 & 0xFF00) | (mb0 & 0xFF);

          m_stencil[xstart] = p;
        }
        iz += izadd;
        ia += iaadd;
      }
      ytop += SCREEN_WIDTH;
      xleft += leftadd;
      xrght += rghtadd;
      zleft += zleftadd;
    }
  }


  /**
   * RGB gouraud
   */
  private void drawsegment_gouraud
  (
    float leftadd,
    float rghtadd,
    int ytop,
    int ybottom
  ) {
    float irf = iradd;
    float igf = igadd;
    float ibf = ibadd;

    ytop*=SCREEN_WIDTH;
    ybottom*=SCREEN_WIDTH;
    int p = m_index;

    while (ytop < ybottom) {
      int xstart = (int) (xleft + PIXEL_CENTER);
      if (xstart < 0)
        xstart = 0;

      int xend = (int) (xrght + PIXEL_CENTER);
      if (xend > SCREEN_WIDTH)
        xend = SCREEN_WIDTH;

      float xdiff = (xstart + PIXEL_CENTER) - xleft;
      int ir = (int) (irf * xdiff + rleft);
      int ig = (int) (igf * xdiff + gleft);
      int ib = (int) (ibf * xdiff + bleft);
      float iz = izadd * xdiff + zleft;

      xstart+=ytop;
      xend+=ytop;

      for ( ; xstart < xend; xstart++ ) {
        if (noDepthTest || (iz <= m_zbuffer[xstart])) {
          m_zbuffer[xstart] = iz;
          m_pixels[xstart]=((ir & 0xFF0000) | ((ig >> 8) & 0xFF00) | (ib >> 16));
          m_stencil[xstart] = p;
        }

        //
        ir+=iradd;
        ig+=igadd;
        ib+=ibadd;
        iz+=izadd;
      }

      ytop+=SCREEN_WIDTH;
      xleft+=leftadd;
      xrght+=rghtadd;
      rleft+=rleftadd;
      gleft+=gleftadd;
      bleft+=bleftadd;
      zleft+=zleftadd;
    }
  }


  /**
   * RGB gouraud + interpolated alpha
   */
  private void drawsegment_gouraud_alpha
  (
    float leftadd,
    float rghtadd,
    int ytop,
    int ybottom
  ) {
    ytop*=SCREEN_WIDTH;
    ybottom*=SCREEN_WIDTH;
    int p = m_index;

    float irf = iradd;
    float igf = igadd;
    float ibf = ibadd;
    float iaf = iaadd;

    while (ytop < ybottom) {
      int xstart = (int) (xleft + PIXEL_CENTER);
      if (xstart < 0)
        xstart = 0;
      int xend = (int) (xrght + PIXEL_CENTER);
      if (xend > SCREEN_WIDTH)
        xend = SCREEN_WIDTH;
      float xdiff = (xstart + PIXEL_CENTER) - xleft;

      int ir = (int) (irf * xdiff + rleft);
      int ig = (int) (igf * xdiff + gleft);
      int ib = (int) (ibf * xdiff + bleft);
      int ia = (int) (iaf * xdiff + aleft);
      float iz = izadd * xdiff + zleft;

      xstart+=ytop;
      xend+=ytop;

      for ( ; xstart < xend; xstart++ ) {
        if (noDepthTest || (iz <= m_zbuffer[xstart])) {
          //m_zbuffer[xstart] = iz;

          //
          int red = (ir & 0xFF0000);
          int grn = (ig >> 8) & 0xFF00;
          int blu = (ib >> 16);

          // get buffer pixels
          int bb = m_pixels[xstart];
          int br = (bb & 0xFF0000);     // 0x00FF0000
          int bg = (bb & 0xFF00);       // 0x0000FF00
          bb = (bb & 0xFF);         // 0x000000FF

          // blend alpha
          int al = ia >> 16;

          //
          m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) | ((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF);
          m_stencil[xstart] = p;
        }

        //
        ir+=iradd;
        ig+=igadd;
        ib+=ibadd;
        ia+=iaadd;
        iz+=izadd;
      }

      ytop+=SCREEN_WIDTH;
      xleft+=leftadd;
      xrght+=rghtadd;
      rleft+=rleftadd;
      gleft+=gleftadd;
      bleft+=bleftadd;
      aleft+=aleftadd;
      zleft+=zleftadd;
    }
  }


  /**
   * 8-bit plain texture
   */
   
   //THIS IS MESSED UP, NEED TO GRAB ORIGINAL VERSION!!!
  private void drawsegment_texture8
  (
    float leftadd,
    float rghtadd,
    int ytop,
    int ybottom
  ) {
  
	//Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details
  	int ypixel = ytop;
    int lastRowStart = m_texture.length - TEX_WIDTH - 2;
	boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES];
	float screenx = 0; float screeny = 0; float screenz = 0;
	float a = 0; float b = 0; float c = 0;
	int linearInterpPower = TEX_INTERP_POWER;
	int linearInterpLength = 1 << linearInterpPower;
	if (accurateMode){
	  if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup
	    newax *= linearInterpLength;
	    newbx *= linearInterpLength;
	    newcx *= linearInterpLength;
	    screenz = nearPlaneDepth;
	    firstSegment = false;
	  } else{
  	    accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate)
	  }
	}
    ytop*=SCREEN_WIDTH;
    ybottom*=SCREEN_WIDTH;
    int p = m_index;

    float iuf = iuadd;
    float ivf = ivadd;

    int red = m_fill & 0xFF0000;
    int grn = m_fill & 0xFF00;
    int blu = m_fill & 0xFF;

    while (ytop < ybottom) {
      int xstart = (int) (xleft + PIXEL_CENTER);
      if (xstart < 0)
        xstart = 0;

	  int xpixel = xstart;//accurate mode

      int xend = (int) (xrght + PIXEL_CENTER);
      if (xend > SCREEN_WIDTH)
        xend = SCREEN_WIDTH;

      float xdiff = (xstart + PIXEL_CENTER) - xleft;
      int iu = (int) (iuf * xdiff + uleft);
      int iv = (int) (ivf * xdiff + vleft);
      float iz = izadd * xdiff + zleft;

      xstart+=ytop;
      xend+=ytop;

	  if (accurateMode){
        screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); 
        screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f));
	    a = screenx*ax+screeny*ay+screenz*az;
	    b = screenx*bx+screeny*by+screenz*bz;
	    c = screenx*cx+screeny*cy+screenz*cz;
	  }
	  boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true;
	  int interpCounter = 0;
	  int deltaU = 0; int deltaV = 0;
	  float fu = 0; float fv = 0;
	  float oldfu = 0; float oldfv = 0;
	  
	  if (accurateMode&&goingIn){
	  	int rightOffset = (xend-xstart-1)%linearInterpLength;
		int leftOffset = linearInterpLength-rightOffset;
        float rightOffset2 = rightOffset / ((float)linearInterpLength);
		float leftOffset2 = leftOffset / ((float)linearInterpLength);
		interpCounter = leftOffset;
    	float ao = a-leftOffset2*newax;
        float bo = b-leftOffset2*newbx;
		float co = c-leftOffset2*newcx;
		float oneoverc = 65536.0f/co;
		oldfu = (ao*oneoverc); oldfv = (bo*oneoverc);
		a += rightOffset2*newax;
		b += rightOffset2*newbx;
		c += rightOffset2*newcx;
		oneoverc = 65536.0f/c;
		fu = a*oneoverc; fv = b*oneoverc;
		deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
		deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
		iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack		
	  } else{
	    float preoneoverc = 65536.0f/c;
	    fu = (a*preoneoverc);
        fv = (b*preoneoverc);
	  }


      for ( ; xstart < xend; xstart++ ) {
	    if(accurateMode){
			    if (interpCounter == linearInterpLength) interpCounter = 0;
				if (interpCounter == 0){
			        a += newax;
                    b += newbx;
                    c += newcx;
					float oneoverc = 65536.0f/c;
					oldfu = fu; oldfv = fv;
					fu = (a*oneoverc); fv = (b*oneoverc);
					iu = (int)oldfu; iv = (int)oldfv;
					deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
					deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
				} else{
				    iu += deltaU;
					iv += deltaV;
				}
				interpCounter++;
		}	
        // try-catch just in case pixel offset it out of range
        try
        {
          if (noDepthTest || (iz <= m_zbuffer[xstart])) {
            //m_zbuffer[xstart] = iz;

            int al0;
            if (m_bilinear) {
              int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16);
              int iui = iu & 0xFFFF;
              al0 = m_texture[ofs] & 0xFF;
              int al1 = m_texture[ofs + 1] & 0xFF;
              if (ofs < lastRowStart) ofs+=TEX_WIDTH;
              int al2 = m_texture[ofs] & 0xFF;
              int al3 = m_texture[ofs + 1] & 0xFF;
              al0 = al0 + (((al1-al0) * iui) >> 16);
              al2 = al2 + (((al3-al2) * iui) >> 16);
              al0 = al0 + (((al2-al0) * (iv & 0xFFFF)) >> 16);
            } else {
              al0 = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)] & 0xFF;
            }

            int br = m_pixels[xstart];
            int bg = (br & 0xFF00);
            int bb = (br & 0xFF);
            br = (br & 0xFF0000);
            m_pixels[xstart] = ((br + (((red - br) * al0) >> 8)) & 0xFF0000) | ((bg + (((grn - bg) * al0) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al0) >> 8)) & 0xFF);
            m_stencil[xstart] = p;
          }
        }
        catch (Exception e) {
        }
        xpixel++;//accurate mode
		if (!accurateMode){
        iu+=iuadd;
        iv+=ivadd;
		}
        iz+=izadd;
      }
 	  ypixel++;//accurate mode
      ytop+=SCREEN_WIDTH;
      xleft+=leftadd;
      xrght+=rghtadd;
      uleft+=uleftadd;
      vleft+=vleftadd;
      zleft+=zleftadd;
    }
  }



  /**
   * 8-bit texutre + alpha
   */
  private void drawsegment_texture8_alpha
  (
    float leftadd,
    float rghtadd,
    int ytop,
    int ybottom
  ) {
  
	//Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details
  	int ypixel = ytop;
    int lastRowStart = m_texture.length - TEX_WIDTH - 2;
	boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES];
	float screenx = 0; float screeny = 0; float screenz = 0;
	float a = 0; float b = 0; float c = 0;
	int linearInterpPower = TEX_INTERP_POWER;
	int linearInterpLength = 1 << linearInterpPower;
	if (accurateMode){
	  if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup
	    newax *= linearInterpLength;
	    newbx *= linearInterpLength;
	    newcx *= linearInterpLength;
	    screenz = nearPlaneDepth;
	    firstSegment = false;
	  } else{
  	    accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate)
	  }
	}
    ytop*=SCREEN_WIDTH;
    ybottom*=SCREEN_WIDTH;
    int p = m_index;

    float iuf = iuadd;
    float ivf = ivadd;
    float iaf = iaadd;

    int red = m_fill & 0xFF0000;
    int grn = m_fill & 0xFF00;
    int blu = m_fill & 0xFF;

    while (ytop < ybottom) {
      int xstart = (int) (xleft + PIXEL_CENTER);
      if (xstart < 0)
        xstart = 0;

	  int xpixel = xstart;//accurate mode

      int xend = (int) (xrght + PIXEL_CENTER);
      if (xend > SCREEN_WIDTH)
        xend = SCREEN_WIDTH;

      float xdiff = (xstart + PIXEL_CENTER) - xleft;
      int iu = (int) (iuf * xdiff + uleft);
      int iv = (int) (ivf * xdiff + vleft);
      int ia = (int) (iaf * xdiff + aleft);
      float iz = izadd * xdiff + zleft;

      xstart+=ytop;
      xend+=ytop;

	  if (accurateMode){
        screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); 
        screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f));
	    a = screenx*ax+screeny*ay+screenz*az;
	    b = screenx*bx+screeny*by+screenz*bz;
	    c = screenx*cx+screeny*cy+screenz*cz;
	  }
	  boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true;
	  int interpCounter = 0;
	  int deltaU = 0; int deltaV = 0;
	  float fu = 0; float fv = 0;
	  float oldfu = 0; float oldfv = 0;
	  
	  if (accurateMode&&goingIn){
	  	int rightOffset = (xend-xstart-1)%linearInterpLength;
		int leftOffset = linearInterpLength-rightOffset;
        float rightOffset2 = rightOffset / ((float)linearInterpLength);
		float leftOffset2 = leftOffset / ((float)linearInterpLength);
		interpCounter = leftOffset;
    	float ao = a-leftOffset2*newax;
        float bo = b-leftOffset2*newbx;
		float co = c-leftOffset2*newcx;
		float oneoverc = 65536.0f/co;
		oldfu = (ao*oneoverc); oldfv = (bo*oneoverc);
		a += rightOffset2*newax;
		b += rightOffset2*newbx;
		c += rightOffset2*newcx;
		oneoverc = 65536.0f/c;
		fu = a*oneoverc; fv = b*oneoverc;
		deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
		deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
		iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack		
	  } else{
	    float preoneoverc = 65536.0f/c;
	    fu = (a*preoneoverc);
        fv = (b*preoneoverc);
	  }


      for ( ; xstart < xend; xstart++ ) {
	    if(accurateMode){
			    if (interpCounter == linearInterpLength) interpCounter = 0;
				if (interpCounter == 0){
			        a += newax;
                    b += newbx;
                    c += newcx;
					float oneoverc = 65536.0f/c;
					oldfu = fu; oldfv = fv;
					fu = (a*oneoverc); fv = (b*oneoverc);
					iu = (int)oldfu; iv = (int)oldfv;
					deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
					deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
				} else{
				    iu += deltaU;
					iv += deltaV;
				}
				interpCounter++;
		}	
        // try-catch just in case pixel offset it out of range
        try
        {
          if (noDepthTest || (iz <= m_zbuffer[xstart])) {
            //m_zbuffer[xstart] = iz;

            int al0;
            if (m_bilinear) {
              int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16);
              int iui = iu & 0xFFFF;
              al0 = m_texture[ofs] & 0xFF;
              int al1 = m_texture[ofs + 1] & 0xFF;
              if (ofs < lastRowStart) ofs+=TEX_WIDTH;
              int al2 = m_texture[ofs] & 0xFF;
              int al3 = m_texture[ofs + 1] & 0xFF;
              al0 = al0 + (((al1-al0) * iui) >> 16);
              al2 = al2 + (((al3-al2) * iui) >> 16);
              al0 = al0 + (((al2-al0) * (iv & 0xFFFF)) >> 16);
            } else {
              al0 = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)] & 0xFF;
            }
            al0 = (al0 * (ia >> 16)) >> 8;

            int br = m_pixels[xstart];
            int bg = (br & 0xFF00);
            int bb = (br & 0xFF);
            br = (br & 0xFF0000);
            m_pixels[xstart] = ((br + (((red - br) * al0) >> 8)) & 0xFF0000) | ((bg + (((grn - bg) * al0) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al0) >> 8)) & 0xFF);
            m_stencil[xstart] = p;
          }
        }
        catch (Exception e) {
        }
        xpixel++;//accurate mode
		if (!accurateMode){
        iu+=iuadd;
        iv+=ivadd;
		}
        iz+=izadd;
        ia+=iaadd;
      }
 	  ypixel++;//accurate mode
      ytop+=SCREEN_WIDTH;
      xleft+=leftadd;
      xrght+=rghtadd;
      uleft+=uleftadd;
      vleft+=vleftadd;
      zleft+=zleftadd;
      aleft+=aleftadd;
    }
  }

  /**
   * Plain 24-bit texture
   */
  private void drawsegment_texture24
  (
    float leftadd,
    float rghtadd,
    int ytop,
    int ybottom
  ) {
    
    ytop*=SCREEN_WIDTH;
    ybottom*=SCREEN_WIDTH;
    int p = m_index;
    float iuf = iuadd;
    float ivf = ivadd;

	int ypixel = ytop/SCREEN_WIDTH;//ACCTEX
    int lastRowStart = m_texture.length - TEX_WIDTH - 2;//If we're past this index, we can't shift down a row w/o throwing an exception
//	int exCount = 0;//counter for exceptions caught
	boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES]; //bring this local since it will be accessed often
	float screenx = 0; float screeny = 0; float screenz = 0;
	float a = 0; float b = 0; float c = 0;
	
	//Interpolation length of 16 tends to look good except at a small angle; 8 looks okay then, except for the
	//above issue.  When viewing close to flat, as high as 32 is often acceptable.  Could add dynamic interpolation
	//settings based on triangle angle - currently we just pick a value and leave it (by default I have the
	//power set at 3, so every 8 pixels a true coordinate is calculated, which seems a decent compromise).
	
	int linearInterpPower = TEX_INTERP_POWER;
	int linearInterpLength = 1 << linearInterpPower;
	if (accurateMode){
	  if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup
	    newax *= linearInterpLength;
	    newbx *= linearInterpLength;
	    newcx *= linearInterpLength;
	    screenz = nearPlaneDepth;
	    firstSegment = false;
	  } else{
  	    accurateMode = false; //if the matrix inversion gave us garbage, revert to normal rendering (something is degenerate)
	  }
	}
	

	while (ytop < ybottom) {//scanline loop
      int xstart = (int) (xleft + PIXEL_CENTER);
	  if (xstart < 0){ xstart = 0; }
	  
	  int xpixel = xstart;//accurate mode

      int xend = (int) (xrght + PIXEL_CENTER);
      if (xend > SCREEN_WIDTH){ xend = SCREEN_WIDTH; }
      float xdiff = (xstart + PIXEL_CENTER) - xleft;
      int iu = (int) (iuf * xdiff + uleft);
      int iv = (int) (ivf * xdiff + vleft);
      float iz = izadd * xdiff + zleft;
      xstart+=ytop;
      xend+=ytop;

	  if (accurateMode){
	  //off by one (half, I guess) hack, w/o it the first rows are outside the texture - maybe a mistake somewhere?
        screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); 
        screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f));
	    a = screenx*ax+screeny*ay+screenz*az;//OPT - some of this could be brought out of the y-loop since
	    b = screenx*bx+screeny*by+screenz*bz;//xpixel and ypixel just increment by the same numbers each iteration.
	    c = screenx*cx+screeny*cy+screenz*cz;//Probably not a big bottleneck, though.
	  }
	  
	  //Figure out whether triangle is going further into the screen or not as we move along scanline
	  boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true;
	  
      //Set up linear interpolation between calculated texture points
	  int interpCounter = 0;
	  int deltaU = 0; int deltaV = 0;
	  //float fdeltaU = 0; float fdeltaV = 0;//vars for floating point interpolating version of algorithm
	  //float fiu = 0; float fiv = 0;
	  float fu = 0; float fv = 0;
	  float oldfu = 0; float oldfv = 0;
	  	

	//Bugfix (done): it's a Really Bad Thing to interpolate along a scanline when the triangle goes deeper into the screen,
	//because if the angle is severe enough the last control point for interpolation may cross the vanishing
	//point.  This leads to some pretty nasty artifacts, and ideally we should scan from right to left if the
	//triangle is better served that way, or (what we do now) precompute the offset that we'll need so that we end up
	//with a control point exactly at the furthest edge of the triangle.

	  if (accurateMode&&goingIn){
	    //IMPORTANT!!!  Results are horrid without this hack!
	    //If polygon goes into the screen along scan line, we want to match the control point to the furthest point drawn
		//since the control points are less meaningful the closer you are to the vanishing point.
		//We'll do this by making the first control point lie before the start of the scanline (safe since it's closer to us)

		int rightOffset = (xend-xstart-1)%linearInterpLength; //"off by one" hack...probably means there's a small bug somewhere
		int leftOffset = linearInterpLength-rightOffset;
        float rightOffset2 = rightOffset / ((float)linearInterpLength);
		float leftOffset2 = leftOffset / ((float)linearInterpLength);
		interpCounter = leftOffset;

        //Take step to control point to the left of start pixel
		float ao = a-leftOffset2*newax;
        float bo = b-leftOffset2*newbx;
		float co = c-leftOffset2*newcx;
		float oneoverc = 65536.0f/co;
		oldfu = (ao*oneoverc); oldfv = (bo*oneoverc);
		
		//Now step to right control point
		a += rightOffset2*newax;
		b += rightOffset2*newbx;
		c += rightOffset2*newcx;
		oneoverc = 65536.0f/c;
		fu = a*oneoverc; fv = b*oneoverc;
		
		//Get deltas for interpolation
		deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
		deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
		iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack		
	  } else{
	    //Otherwise the left edge is further, and we pin the first control point to it
	    float preoneoverc = 65536.0f/c;
	    fu = (a*preoneoverc);
        fv = (b*preoneoverc);
	  }
	  
	  for ( ; xstart < xend; xstart++ ) {//pixel loop - keep trim, can execute thousands of times per frame
        //boolean drawBlack = false; //used to display control points
		if(accurateMode){
			/*  //Non-interpolating algorithm - slowest version, calculates exact coordinate for each pixel,
			    //and casts from float->int
			    float oneoverc = 65536.0f/c;  //a bit faster to pre-divide for next two steps
				iu = (int)(a*oneoverc);
                iv = (int)(b*oneoverc);
			    a += newax;
                b += newbx;
                c += newcx;
		    */
				
				//Float while calculating, int while interpolating
				if (interpCounter == linearInterpLength) interpCounter = 0;
				if (interpCounter == 0){
				    //drawBlack = true;
			        a += newax;
                    b += newbx;
                    c += newcx;
					float oneoverc = 65536.0f/c;
					oldfu = fu; oldfv = fv;
					fu = (a*oneoverc); fv = (b*oneoverc);
					iu = (int)oldfu; iv = (int)oldfv; //ints are used for interpolation, not actual computation
					deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
					deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
				} else{        //race through using linear interpolation if we're not at a control point
				    iu += deltaU;
					iv += deltaV;
				}
				interpCounter++;
				
			/* //Floating point interpolating version - slower than int thanks to casts during interpolation steps
			if (interpCounter == 0) { 
				interpCounter = linearInterpLength;
				a += newax;
				b += newbx;
				c += newcx;
				float oneoverc = 65536.0f/c;
				oldfu = fu; 
				oldfv = fv;
				fu = (a*oneoverc); 
				fv = (b*oneoverc);
				//oldu = u; oldv = v;
				fiu = oldfu;
				fiv = oldfv;
                fdeltaU = (fu-oldfu)/linearInterpLength;
				fdeltaV = (fv-oldfv)/linearInterpLength;
			} 
			else{
				fiu += fdeltaU;
				fiv += fdeltaV;
			}
			interpCounter--;
			iu = (int)(fiu); iv = (int)(fiv);*/
				
		}	
			  
        // try-catch just in case pixel offset is out of range
	    try{
          if (noDepthTest || (iz <= m_zbuffer[xstart])) {
            m_zbuffer[xstart] = iz;
            if (m_bilinear) {
                //We could (should?) add bounds checking on iu and iv here (keep in mind the 16 bit shift!).
				//This would also be the place to add looping texture mode (bounds check == clamped).
				//For looping/clamped textures, we'd also need to change PGraphics.textureVertex() to remove
				//the texture range check there (it constrains normalized texture coordinates from 0->1).

              int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16);
              int iui = (iu & 0xFFFF) >> 9;
              int ivi = (iv & 0xFFFF) >> 9;

				//if(ofs < 0) { ofs += TEX_WIDTH; }
				//if(ofs > m_texture.length-2) {ofs -= TEX_WIDTH; }

              // get texture pixels
              int pix0 = m_texture[ofs];
              int pix1 = m_texture[ofs + 1];
                if (ofs < lastRowStart) ofs+=TEX_WIDTH; //quick hack to thwart exceptions
              int pix2 = m_texture[ofs];
              int pix3 = m_texture[ofs + 1];

              // red
              int red0 = (pix0 & 0xFF0000);
              int red2 = (pix2 & 0xFF0000);
              int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7);
              int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7);
              int red = up + (((dn-up) * ivi) >> 7);

              // grn
              red0 = (pix0 & 0xFF00);
              red2 = (pix2 & 0xFF00);
              up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7);
              dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7);
              int grn = up + (((dn-up) * ivi) >> 7);

              // blu
              red0 = (pix0 & 0xFF);
              red2 = (pix2 & 0xFF);
              up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7);
              dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7);
              int blu = up + (((dn-up) * ivi) >> 7);

              m_pixels[xstart] = (red & 0xFF0000) | (grn & 0xFF00) | (blu & 0xFF);
				//if (drawBlack){ m_pixels[xstart] = 0; }
				
			} else{
              m_pixels[xstart] = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)];
            }
            m_stencil[xstart] = p;
          }
        } catch (Exception e) {/*exCount++;*/}
		iz+=izadd;
		xpixel++;//accurate mode
		if (!accurateMode){
        iu+=iuadd;
        iv+=ivadd;
		}
	   }
	   ypixel++;//accurate mode
      ytop+=SCREEN_WIDTH;
      xleft+=leftadd;
      xrght+=rghtadd;
       zleft+=zleftadd;
      uleft+=uleftadd;
      vleft+=vleftadd;
	}
//	if (exCount>0) System.out.println(exCount+" exceptions in this segment");
  }

 
  /**
   * Alpha 24-bit texture
   */
  private void drawsegment_texture24_alpha
  (
    float leftadd,
    float rghtadd,
    int ytop,
    int ybottom
  ) {
  //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details
  	int ypixel = ytop;
    int lastRowStart = m_texture.length - TEX_WIDTH - 2;
	boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES];
	float screenx = 0; float screeny = 0; float screenz = 0;
	float a = 0; float b = 0; float c = 0;
	int linearInterpPower = TEX_INTERP_POWER;
	int linearInterpLength = 1 << linearInterpPower;
	if (accurateMode){
	  if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup
	    newax *= linearInterpLength;
	    newbx *= linearInterpLength;
	    newcx *= linearInterpLength;
	    screenz = nearPlaneDepth;
	    firstSegment = false;
	  } else{
  	    accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate)
	  }
	}
	
    ytop*=SCREEN_WIDTH;
    ybottom*=SCREEN_WIDTH;
    int p = m_index;

    float iuf = iuadd;
    float ivf = ivadd;
    float iaf = iaadd;

    while (ytop < ybottom) {
      int xstart = (int) (xleft + PIXEL_CENTER);
      if (xstart < 0)
        xstart = 0;

	  int xpixel = xstart;//accurate mode
	  
      int xend = (int) (xrght + PIXEL_CENTER);
      if (xend > SCREEN_WIDTH)
        xend = SCREEN_WIDTH;

      float xdiff = (xstart + PIXEL_CENTER) - xleft;
      int iu = (int) (iuf * xdiff + uleft);
      int iv = (int) (ivf * xdiff + vleft);
      int ia = (int) (iaf * xdiff + aleft);
      float iz = izadd * xdiff + zleft;

      xstart+=ytop;
      xend+=ytop;
      
	  if (accurateMode){
        screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); 
        screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f));
	    a = screenx*ax+screeny*ay+screenz*az;
	    b = screenx*bx+screeny*by+screenz*bz;
	    c = screenx*cx+screeny*cy+screenz*cz;
	  }
	  boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true;
	  int interpCounter = 0;
	  int deltaU = 0; int deltaV = 0;
	  float fu = 0; float fv = 0;
	  float oldfu = 0; float oldfv = 0;
	  
	  if (accurateMode&&goingIn){
	  	int rightOffset = (xend-xstart-1)%linearInterpLength;
		int leftOffset = linearInterpLength-rightOffset;
        float rightOffset2 = rightOffset / ((float)linearInterpLength);
		float leftOffset2 = leftOffset / ((float)linearInterpLength);
		interpCounter = leftOffset;
    	float ao = a-leftOffset2*newax;
        float bo = b-leftOffset2*newbx;
		float co = c-leftOffset2*newcx;
		float oneoverc = 65536.0f/co;
		oldfu = (ao*oneoverc); oldfv = (bo*oneoverc);
		a += rightOffset2*newax;
		b += rightOffset2*newbx;
		c += rightOffset2*newcx;
		oneoverc = 65536.0f/c;
		fu = a*oneoverc; fv = b*oneoverc;
		deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
		deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
		iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack		
	  } else{
	    float preoneoverc = 65536.0f/c;
	    fu = (a*preoneoverc);
        fv = (b*preoneoverc);
	  }
	  

      for ( ; xstart < xend; xstart++ ) {
		if(accurateMode){
			    if (interpCounter == linearInterpLength) interpCounter = 0;
				if (interpCounter == 0){
			        a += newax;
                    b += newbx;
                    c += newcx;
					float oneoverc = 65536.0f/c;
					oldfu = fu; oldfv = fv;
					fu = (a*oneoverc); fv = (b*oneoverc);
					iu = (int)oldfu; iv = (int)oldfv;
					deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
					deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
				} else{
				    iu += deltaU;
					iv += deltaV;
				}
				interpCounter++;
		}	

        try
        {
          if (noDepthTest || (iz <= m_zbuffer[xstart])) {
            //m_zbuffer[xstart] = iz;

            // get alpha
            int al = ia >> 16;

            if (m_bilinear) {
              int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16);
              int iui = (iu & 0xFFFF) >> 9;
              int ivi = (iv & 0xFFFF) >> 9;

              // get texture pixels
              int pix0 = m_texture[ofs];
              int pix1 = m_texture[ofs + 1];
			  if (ofs < lastRowStart) ofs+=TEX_WIDTH;
              int pix2 = m_texture[ofs];
              int pix3 = m_texture[ofs + 1];

              // red
              int red0 = (pix0 & 0xFF0000);
              int red2 = (pix2 & 0xFF0000);
              int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7);
              int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7);
              int red = up + (((dn-up) * ivi) >> 7);

              // grn
              red0 = (pix0 & 0xFF00);
              red2 = (pix2 & 0xFF00);
              up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7);
              dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7);
              int grn = up + (((dn-up) * ivi) >> 7);

              // blu
              red0 = (pix0 & 0xFF);
              red2 = (pix2 & 0xFF);
              up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7);
              dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7);
              int blu = up + (((dn-up) * ivi) >> 7);

              // get buffer pixels
              int bb = m_pixels[xstart];
              int br = (bb & 0xFF0000);     // 0x00FF0000
              int bg = (bb & 0xFF00);       // 0x0000FF00
              bb = (bb & 0xFF);         // 0x000000FF
              m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |( (bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF);
            } else {
              int red = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)];
              int grn = red & 0xFF00;
              int blu = red & 0xFF;
              red&=0xFF0000;

              // get buffer pixels
              int bb = m_pixels[xstart];
              int br = (bb & 0xFF0000);     // 0x00FF0000
              int bg = (bb & 0xFF00);       // 0x0000FF00
              bb = (bb & 0xFF);         // 0x000000FF
              m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF);
            }
            m_stencil[xstart] = p;
          }
        }
        catch (Exception e) {}
        xpixel++;//accurate mode
		if (!accurateMode){
        iu+=iuadd;
        iv+=ivadd;
		}
        ia+=iaadd;
        iz+=izadd;
      }
	  ypixel++;//accurate mode

      ytop+=SCREEN_WIDTH;

      xleft+=leftadd;
      xrght+=rghtadd;
      uleft+=uleftadd;
      vleft+=vleftadd;
      zleft+=zleftadd;
      aleft+=aleftadd;
    }
  }

  /**
   * Plain 32-bit texutre
   */
  private void drawsegment_texture32
  (
    float leftadd,
    float rghtadd,
    int ytop,
    int ybottom
  ) {
    //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details
  	int ypixel = ytop;
    int lastRowStart = m_texture.length - TEX_WIDTH - 2;
	boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES];
	float screenx = 0; float screeny = 0; float screenz = 0;
	float a = 0; float b = 0; float c = 0;
	int linearInterpPower = TEX_INTERP_POWER;
	int linearInterpLength = 1 << linearInterpPower;
	if (accurateMode){
	  if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup
	    newax *= linearInterpLength;
	    newbx *= linearInterpLength;
	    newcx *= linearInterpLength;
	    screenz = nearPlaneDepth;
	    firstSegment = false;
	  } else{
  	    accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate)
	  }
	}
	
    ytop*=SCREEN_WIDTH;
    ybottom*=SCREEN_WIDTH;
    int p = m_index;

    float iuf = iuadd;
    float ivf = ivadd;

    while (ytop < ybottom) {
      int xstart = (int) (xleft + PIXEL_CENTER);
      if (xstart < 0)
        xstart = 0;
		
	  int xpixel = xstart;//accurate mode

      int xend = (int) (xrght + PIXEL_CENTER);
      if (xend > SCREEN_WIDTH)
        xend = SCREEN_WIDTH;

      float xdiff = (xstart + PIXEL_CENTER) - xleft;
      int iu = (int) (iuf * xdiff + uleft);
      int iv = (int) (ivf * xdiff + vleft);
      float iz = izadd * xdiff + zleft;

      xstart+=ytop;
      xend+=ytop;

	  	  if (accurateMode){
        screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); 
        screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f));
	    a = screenx*ax+screeny*ay+screenz*az;
	    b = screenx*bx+screeny*by+screenz*bz;
	    c = screenx*cx+screeny*cy+screenz*cz;
	  }
	  boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true;
	  int interpCounter = 0;
	  int deltaU = 0; int deltaV = 0;
	  float fu = 0; float fv = 0;
	  float oldfu = 0; float oldfv = 0;
	  
	  if (accurateMode&&goingIn){
	  	int rightOffset = (xend-xstart-1)%linearInterpLength;
		int leftOffset = linearInterpLength-rightOffset;
        float rightOffset2 = rightOffset / ((float)linearInterpLength);
		float leftOffset2 = leftOffset / ((float)linearInterpLength);
		interpCounter = leftOffset;
    	float ao = a-leftOffset2*newax;
        float bo = b-leftOffset2*newbx;
		float co = c-leftOffset2*newcx;
		float oneoverc = 65536.0f/co;
		oldfu = (ao*oneoverc); oldfv = (bo*oneoverc);
		a += rightOffset2*newax;
		b += rightOffset2*newbx;
		c += rightOffset2*newcx;
		oneoverc = 65536.0f/c;
		fu = a*oneoverc; fv = b*oneoverc;
		deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
		deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
		iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack		
	  } else{
	    float preoneoverc = 65536.0f/c;
	    fu = (a*preoneoverc);
        fv = (b*preoneoverc);
	  }


      for ( ; xstart < xend; xstart++ ) {
	    if(accurateMode){
			    if (interpCounter == linearInterpLength) interpCounter = 0;
				if (interpCounter == 0){
			        a += newax;
                    b += newbx;
                    c += newcx;
					float oneoverc = 65536.0f/c;
					oldfu = fu; oldfv = fv;
					fu = (a*oneoverc); fv = (b*oneoverc);
					iu = (int)oldfu; iv = (int)oldfv;
					deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
					deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
				} else{
				    iu += deltaU;
					iv += deltaV;
				}
				interpCounter++;
		}	
	  
        // try-catch just in case pixel offset it out of range
        try
        {
          if (noDepthTest || (iz <= m_zbuffer[xstart])) {
            //m_zbuffer[xstart] = iz;

            if (m_bilinear) {
              int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16);
              int iui = (iu & 0xFFFF) >> 9;
              int ivi = (iv & 0xFFFF) >> 9;

              // get texture pixels
              int pix0 = m_texture[ofs];
              int pix1 = m_texture[ofs + 1];
              if (ofs < lastRowStart) ofs+=TEX_WIDTH;
              int pix2 = m_texture[ofs];
              int pix3 = m_texture[ofs + 1];

              // red
              int red0 = (pix0 & 0xFF0000);
              int red2 = (pix2 & 0xFF0000);
              int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7);
              int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7);
              int red = up + (((dn-up) * ivi) >> 7);

              // grn
              red0 = (pix0 & 0xFF00);
              red2 = (pix2 & 0xFF00);
              up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7);
              dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7);
              int grn = up + (((dn-up) * ivi) >> 7);

              // blu
              red0 = (pix0 & 0xFF);
              red2 = (pix2 & 0xFF);
              up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7);
              dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7);
              int blu = up + (((dn-up) * ivi) >> 7);

              // alpha
              pix0>>>=24;
              pix2>>>=24;
              up = pix0 + ((((pix1 >>> 24) - pix0) * iui) >> 7);
              dn = pix2 + ((((pix3 >>> 24) - pix2) * iui) >> 7);
              int al = up + (((dn-up) * ivi) >> 7);

              // get buffer pixels
              int bb = m_pixels[xstart];
              int br = (bb & 0xFF0000);     // 0x00FF0000
              int bg = (bb & 0xFF00);       // 0x0000FF00
              bb = (bb & 0xFF);         // 0x000000FF
              m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF);
            } else {
              int red = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)];
              int al = red >>> 24;
              int grn = red & 0xFF00;
              int blu = red & 0xFF;
              red&=0xFF0000;

              // get buffer pixels
              int bb = m_pixels[xstart];
              int br = (bb & 0xFF0000);     // 0x00FF0000
              int bg = (bb & 0xFF00);       // 0x0000FF00
              bb = (bb & 0xFF);         // 0x000000FF
              m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF);
            }
            m_stencil[xstart] = p;
          }
        }
        catch (Exception e) {
        }
        xpixel++;//accurate mode
		if (!accurateMode){
        iu+=iuadd;
        iv+=ivadd;
		}
        iz+=izadd;
      }
 	  ypixel++;//accurate mode

      ytop+=SCREEN_WIDTH;

      xleft+=leftadd;
      xrght+=rghtadd;
      uleft+=uleftadd;
      vleft+=vleftadd;
      zleft+=zleftadd;
      aleft+=aleftadd;
    }


  }

  /**
   * Alpha 32-bit texutre
   */
  private void drawsegment_texture32_alpha
  (
    float leftadd,
    float rghtadd,
    int ytop,
    int ybottom
  ) {
    //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details
  	int ypixel = ytop;
    int lastRowStart = m_texture.length - TEX_WIDTH - 2;
	boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES];
	float screenx = 0; float screeny = 0; float screenz = 0;
	float a = 0; float b = 0; float c = 0;
	int linearInterpPower = TEX_INTERP_POWER;
	int linearInterpLength = 1 << linearInterpPower;
	if (accurateMode){
	  if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup
	    newax *= linearInterpLength;
	    newbx *= linearInterpLength;
	    newcx *= linearInterpLength;
	    screenz = nearPlaneDepth;
	    firstSegment = false;
	  } else{
  	    accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate)
	  }
	}

    ytop*=SCREEN_WIDTH;
    ybottom*=SCREEN_WIDTH;
    int p = m_index;

    float iuf = iuadd;
    float ivf = ivadd;
    float iaf = iaadd;

    while (ytop < ybottom) {
      int xstart = (int) (xleft + PIXEL_CENTER);
      if (xstart < 0)
        xstart = 0;

	  int xpixel = xstart;//accurate mode

      int xend = (int) (xrght + PIXEL_CENTER);
      if (xend > SCREEN_WIDTH)
        xend = SCREEN_WIDTH;

      float xdiff = (xstart + PIXEL_CENTER) - xleft;
      int iu = (int) (iuf * xdiff + uleft);
      int iv = (int) (ivf * xdiff + vleft);
      int ia = (int) (iaf * xdiff + aleft);
      float iz = izadd * xdiff + zleft;

      xstart+=ytop;
      xend+=ytop;

	  	  if (accurateMode){
        screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); 
        screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f));
	    a = screenx*ax+screeny*ay+screenz*az;
	    b = screenx*bx+screeny*by+screenz*bz;
	    c = screenx*cx+screeny*cy+screenz*cz;
	  }
	  boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true;
	  int interpCounter = 0;
	  int deltaU = 0; int deltaV = 0;
	  float fu = 0; float fv = 0;
	  float oldfu = 0; float oldfv = 0;
	  
	  if (accurateMode&&goingIn){
	  	int rightOffset = (xend-xstart-1)%linearInterpLength;
		int leftOffset = linearInterpLength-rightOffset;
        float rightOffset2 = rightOffset / ((float)linearInterpLength);
		float leftOffset2 = leftOffset / ((float)linearInterpLength);
		interpCounter = leftOffset;
    	float ao = a-leftOffset2*newax;
        float bo = b-leftOffset2*newbx;
		float co = c-leftOffset2*newcx;
		float oneoverc = 65536.0f/co;
		oldfu = (ao*oneoverc); oldfv = (bo*oneoverc);
		a += rightOffset2*newax;
		b += rightOffset2*newbx;
		c += rightOffset2*newcx;
		oneoverc = 65536.0f/c;
		fu = a*oneoverc; fv = b*oneoverc;
		deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
		deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
		iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack		
	  } else{
	    float preoneoverc = 65536.0f/c;
	    fu = (a*preoneoverc);
        fv = (b*preoneoverc);
	  }
	  


      for ( ; xstart < xend; xstart++ ) {
		if(accurateMode){
			    if (interpCounter == linearInterpLength) interpCounter = 0;
				if (interpCounter == 0){
			        a += newax;
                    b += newbx;
                    c += newcx;
					float oneoverc = 65536.0f/c;
					oldfu = fu; oldfv = fv;
					fu = (a*oneoverc); fv = (b*oneoverc);
					iu = (int)oldfu; iv = (int)oldfv;
					deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
					deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
				} else{
				    iu += deltaU;
					iv += deltaV;
				}
				interpCounter++;
		}	

        // try-catch just in case pixel offset it out of range
        try
        {
          if (noDepthTest || (iz <= m_zbuffer[xstart])) {
            //m_zbuffer[xstart] = iz;

            // get alpha
            int al = ia >> 16;

            if (m_bilinear) {
              int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16);
              int iui = (iu & 0xFFFF) >> 9;
              int ivi = (iv & 0xFFFF) >> 9;

              // get texture pixels
              int pix0 = m_texture[ofs];
              int pix1 = m_texture[ofs + 1];
              if (ofs < lastRowStart) ofs+=TEX_WIDTH;
              int pix2 = m_texture[ofs];
              int pix3 = m_texture[ofs + 1];

              // red
              int red0 = (pix0 & 0xFF0000);
              int red2 = (pix2 & 0xFF0000);
              int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7);
              int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7);
              int red = up + (((dn-up) * ivi) >> 7);

              // grn
              red0 = (pix0 & 0xFF00);
              red2 = (pix2 & 0xFF00);
              up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7);
              dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7);
              int grn = up + (((dn-up) * ivi) >> 7);

              // blu
              red0 = (pix0 & 0xFF);
              red2 = (pix2 & 0xFF);
              up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7);
              dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7);
              int blu = up + (((dn-up) * ivi) >> 7);

              // alpha
              pix0>>>=24;
              pix2>>>=24;
              up = pix0 + ((((pix1 >>> 24) - pix0) * iui) >> 7);
              dn = pix2 + ((((pix3 >>> 24) - pix2) * iui) >> 7);
              al = al * (up + (((dn-up) * ivi) >> 7)) >> 8;

              // get buffer pixels
              int bb = m_pixels[xstart];
              int br = (bb & 0xFF0000);     // 0x00FF0000
              int bg = (bb & 0xFF00);       // 0x0000FF00
              bb = (bb & 0xFF);         // 0x000000FF
              m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF);
            } else {
              int red = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)];
              al = al * (red >>> 24) >> 8;
              int grn = red & 0xFF00;
              int blu = red & 0xFF;
              red&=0xFF0000;

              // get buffer pixels
              int bb = m_pixels[xstart];
              int br = (bb & 0xFF0000);     // 0x00FF0000
              int bg = (bb & 0xFF00);       // 0x0000FF00
              bb = (bb & 0xFF);         // 0x000000FF
              m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF);
            }
            m_stencil[xstart] = p;
          }
        }
        catch (Exception e) {
        }
        xpixel++;//accurate mode
		if (!accurateMode){
        iu+=iuadd;
        iv+=ivadd;
		}
        ia+=iaadd;
        iz+=izadd;
      }
	  ypixel++;//accurate mode

      ytop+=SCREEN_WIDTH;

      xleft+=leftadd;
      xrght+=rghtadd;
      uleft+=uleftadd;
      vleft+=vleftadd;
      zleft+=zleftadd;
      aleft+=aleftadd;
    }


  }


  /**
   * Gouraud blended with 8-bit alpha texture
   */
  private void drawsegment_gouraud_texture8
  (
    float leftadd,
    float rghtadd,
    int ytop,
    int ybottom
  ) {
    //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details
  	int ypixel = ytop;
    int lastRowStart = m_texture.length - TEX_WIDTH - 2;
	boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES];
	float screenx = 0; float screeny = 0; float screenz = 0;
	float a = 0; float b = 0; float c = 0;
	int linearInterpPower = TEX_INTERP_POWER;
	int linearInterpLength = 1 << linearInterpPower;
	if (accurateMode){
	  if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup
	    newax *= linearInterpLength;
	    newbx *= linearInterpLength;
	    newcx *= linearInterpLength;
	    screenz = nearPlaneDepth;
	    firstSegment = false;
	  } else{
  	    accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate)
	  }
	}

    ytop*=SCREEN_WIDTH;
    ybottom*=SCREEN_WIDTH;
    int p = m_index;

    float iuf = iuadd;
    float ivf = ivadd;
    float irf = iradd;
    float igf = igadd;
    float ibf = ibadd;

    while (ytop < ybottom) {
      int xstart = (int) (xleft + PIXEL_CENTER);
      if (xstart < 0)
        xstart = 0;
		
	  int xpixel = xstart;//accurate mode

      int xend = (int) (xrght + PIXEL_CENTER);
      if (xend > SCREEN_WIDTH)
        xend = SCREEN_WIDTH;
      float xdiff = (xstart + PIXEL_CENTER) - xleft;

      int iu = (int) (iuf * xdiff + uleft);
      int iv = (int) (ivf * xdiff + vleft);
      int ir = (int) (irf * xdiff + rleft);
      int ig = (int) (igf * xdiff + gleft);
      int ib = (int) (ibf * xdiff + bleft);
      float iz = izadd * xdiff + zleft;

      xstart+=ytop;
      xend+=ytop;

	  if (accurateMode){
        screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); 
        screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f));
	    a = screenx*ax+screeny*ay+screenz*az;
	    b = screenx*bx+screeny*by+screenz*bz;
	    c = screenx*cx+screeny*cy+screenz*cz;
	  }
	  boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true;
	  int interpCounter = 0;
	  int deltaU = 0; int deltaV = 0;
	  float fu = 0; float fv = 0;
	  float oldfu = 0; float oldfv = 0;
	  
	  if (accurateMode&&goingIn){
	  	int rightOffset = (xend-xstart-1)%linearInterpLength;
		int leftOffset = linearInterpLength-rightOffset;
        float rightOffset2 = rightOffset / ((float)linearInterpLength);
		float leftOffset2 = leftOffset / ((float)linearInterpLength);
		interpCounter = leftOffset;
    	float ao = a-leftOffset2*newax;
        float bo = b-leftOffset2*newbx;
		float co = c-leftOffset2*newcx;
		float oneoverc = 65536.0f/co;
		oldfu = (ao*oneoverc); oldfv = (bo*oneoverc);
		a += rightOffset2*newax;
		b += rightOffset2*newbx;
		c += rightOffset2*newcx;
		oneoverc = 65536.0f/c;
		fu = a*oneoverc; fv = b*oneoverc;
		deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
		deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
		iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack		
	  } else{
	    float preoneoverc = 65536.0f/c;
	    fu = (a*preoneoverc);
        fv = (b*preoneoverc);
	  }


      for ( ; xstart < xend; xstart++ ) {
	    if(accurateMode){
			    if (interpCounter == linearInterpLength) interpCounter = 0;
				if (interpCounter == 0){
			        a += newax;
                    b += newbx;
                    c += newcx;
					float oneoverc = 65536.0f/c;
					oldfu = fu; oldfv = fv;
					fu = (a*oneoverc); fv = (b*oneoverc);
					iu = (int)oldfu; iv = (int)oldfv;
					deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
					deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
				} else{
				    iu += deltaU;
					iv += deltaV;
				}
				interpCounter++;
		}	

        try
        {
          if (noDepthTest || (iz <= m_zbuffer[xstart])) {
            //m_zbuffer[xstart] = iz;

            int al0;
            if (m_bilinear) {
              int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16);
              int iui = iu & 0xFFFF;
              al0 = m_texture[ofs] & 0xFF;
              int al1 = m_texture[ofs + 1] & 0xFF;
              if (ofs < lastRowStart) ofs+=TEX_WIDTH;
              int al2 = m_texture[ofs] & 0xFF;
              int al3 = m_texture[ofs + 1] & 0xFF;
              al0 = al0 + (((al1-al0) * iui) >> 16);
              al2 = al2 + (((al3-al2) * iui) >> 16);
              al0 = al0 + (((al2-al0) * (iv & 0xFFFF)) >> 16);
            } else {
              al0 = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)] & 0xFF;
            }

            // get RGB colors
            int red = ir & 0xFF0000;
            int grn = (ig >> 8) & 0xFF00;
            int blu = (ib >> 16);

            // get buffer pixels
            int bb = m_pixels[xstart];
            int br = (bb & 0xFF0000);     // 0x00FF0000
            int bg = (bb & 0xFF00);       // 0x0000FF00
            bb = (bb & 0xFF);         // 0x000000FF
            m_pixels[xstart] = ((br + (((red - br) * al0) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al0) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al0) >> 8)) & 0xFF);

            // write stencil
            m_stencil[xstart] = p;
          }
        }
        catch (Exception e) {

        }

        //
        xpixel++;//accurate mode
		if (!accurateMode){
        iu+=iuadd;
        iv+=ivadd;
		}
        ir+=iradd;
        ig+=igadd;
        ib+=ibadd;
        iz+=izadd;
      }
 	  ypixel++;//accurate mode
      ytop+=SCREEN_WIDTH;
      xleft+=leftadd;
      xrght+=rghtadd;

      uleft+=uleftadd;
      vleft+=vleftadd;
      rleft+=rleftadd;
      gleft+=gleftadd;
      bleft+=bleftadd;
      zleft+=zleftadd;
    }
  }


  /**
   * Texture multiplied with gouraud
   */
  private void drawsegment_gouraud_texture8_alpha
  (
    float leftadd,
    float rghtadd,
    int ytop,
    int ybottom
  ) {
    //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details
  	int ypixel = ytop;
    int lastRowStart = m_texture.length - TEX_WIDTH - 2;
	boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES];
	float screenx = 0; float screeny = 0; float screenz = 0;
	float a = 0; float b = 0; float c = 0;
	int linearInterpPower = TEX_INTERP_POWER;
	int linearInterpLength = 1 << linearInterpPower;
	if (accurateMode){
	  if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup
	    newax *= linearInterpLength;
	    newbx *= linearInterpLength;
	    newcx *= linearInterpLength;
	    screenz = nearPlaneDepth;
	    firstSegment = false;
	  } else{
  	    accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate)
	  }
	}

    ytop*=SCREEN_WIDTH;
    ybottom*=SCREEN_WIDTH;
    int p = m_index;

    float iuf = iuadd;
    float ivf = ivadd;
    float irf = iradd;
    float igf = igadd;
    float ibf = ibadd;
    float iaf = iaadd;

    while (ytop < ybottom) {
      int xstart = (int) (xleft + PIXEL_CENTER);
      if (xstart < 0)
        xstart = 0;

	  int xpixel = xstart;//accurate mode

      int xend = (int) (xrght + PIXEL_CENTER);
      if (xend > SCREEN_WIDTH)
        xend = SCREEN_WIDTH;
      float xdiff = (xstart + PIXEL_CENTER) - xleft;

      int iu = (int) (iuf * xdiff + uleft);
      int iv = (int) (ivf * xdiff + vleft);
      int ir = (int) (irf * xdiff + rleft);
      int ig = (int) (igf * xdiff + gleft);
      int ib = (int) (ibf * xdiff + bleft);
      int ia = (int) (iaf * xdiff + aleft);
      float iz = izadd * xdiff + zleft;

      xstart+=ytop;
      xend+=ytop;

	  if (accurateMode){
        screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); 
        screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f));
	    a = screenx*ax+screeny*ay+screenz*az;
	    b = screenx*bx+screeny*by+screenz*bz;
	    c = screenx*cx+screeny*cy+screenz*cz;
	  }
	  boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true;
	  int interpCounter = 0;
	  int deltaU = 0; int deltaV = 0;
	  float fu = 0; float fv = 0;
	  float oldfu = 0; float oldfv = 0;
	  
	  if (accurateMode&&goingIn){
	  	int rightOffset = (xend-xstart-1)%linearInterpLength;
		int leftOffset = linearInterpLength-rightOffset;
        float rightOffset2 = rightOffset / ((float)linearInterpLength);
		float leftOffset2 = leftOffset / ((float)linearInterpLength);
		interpCounter = leftOffset;
    	float ao = a-leftOffset2*newax;
        float bo = b-leftOffset2*newbx;
		float co = c-leftOffset2*newcx;
		float oneoverc = 65536.0f/co;
		oldfu = (ao*oneoverc); oldfv = (bo*oneoverc);
		a += rightOffset2*newax;
		b += rightOffset2*newbx;
		c += rightOffset2*newcx;
		oneoverc = 65536.0f/c;
		fu = a*oneoverc; fv = b*oneoverc;
		deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
		deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
		iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack		
	  } else{
	    float preoneoverc = 65536.0f/c;
	    fu = (a*preoneoverc);
        fv = (b*preoneoverc);
	  }


      for ( ; xstart < xend; xstart++ ) {
		if(accurateMode){
			    if (interpCounter == linearInterpLength) interpCounter = 0;
				if (interpCounter == 0){
			        a += newax;
                    b += newbx;
                    c += newcx;
					float oneoverc = 65536.0f/c;
					oldfu = fu; oldfv = fv;
					fu = (a*oneoverc); fv = (b*oneoverc);
					iu = (int)oldfu; iv = (int)oldfv;
					deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
					deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
				} else{
				    iu += deltaU;
					iv += deltaV;
				}
				interpCounter++;
		}	

        try
        {
          if (noDepthTest || (iz <= m_zbuffer[xstart])) {
            //m_zbuffer[xstart] = iz;

            int al0;
            if (m_bilinear) {
              int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16);
              int iui = iu & 0xFFFF;
              al0 = m_texture[ofs] & 0xFF;
              int al1 = m_texture[ofs + 1] & 0xFF;
              if (ofs < lastRowStart) ofs+=TEX_WIDTH;
              int al2 = m_texture[ofs] & 0xFF;
              int al3 = m_texture[ofs + 1] & 0xFF;
              al0 = al0 + (((al1-al0) * iui) >> 16);
              al2 = al2 + (((al3-al2) * iui) >> 16);
              al0 = al0 + (((al2-al0) * (iv & 0xFFFF)) >> 16);
            } else {
              al0 = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)] & 0xFF;
            }
            al0 = (al0 * (ia >> 16)) >> 8;

            // get RGB colors
            int red = ir & 0xFF0000;
            int grn = (ig >> 8) & 0xFF00;
            int blu = (ib >> 16);

            // get buffer pixels
            int bb = m_pixels[xstart];
            int br = (bb & 0xFF0000);     // 0x00FF0000
            int bg = (bb & 0xFF00);       // 0x0000FF00
            bb = (bb & 0xFF);         // 0x000000FF
            m_pixels[xstart] = ((br + (((red - br) * al0) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al0) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al0) >> 8)) & 0xFF);

            // write stencil
            m_stencil[xstart] = p;
          }
        }
        catch (Exception e) {

        }

        //
        xpixel++;//accurate mode
		if (!accurateMode){
        iu+=iuadd;
        iv+=ivadd;
		}
        ir+=iradd;
        ig+=igadd;
        ib+=ibadd;
        ia+=iaadd;
        iz+=izadd;
      }
 	  ypixel++;//accurate mode
      ytop+=SCREEN_WIDTH;
      xleft+=leftadd;
      xrght+=rghtadd;
      uleft+=uleftadd;
      vleft+=vleftadd;
      rleft+=rleftadd;
      gleft+=gleftadd;
      bleft+=bleftadd;
      aleft+=aleftadd;
      zleft+=zleftadd;
    }
  }


  /**
   * Texture multiplied with gouraud
   */
  private void drawsegment_gouraud_texture24
  (
    float leftadd,
    float rghtadd,
    int ytop,
    int ybottom
  ) {
    //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details
  	int ypixel = ytop;
    int lastRowStart = m_texture.length - TEX_WIDTH - 2;
	boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES];
	float screenx = 0; float screeny = 0; float screenz = 0;
	float a = 0; float b = 0; float c = 0;
	int linearInterpPower = TEX_INTERP_POWER;
	int linearInterpLength = 1 << linearInterpPower;
	if (accurateMode){
	  if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup
	    newax *= linearInterpLength;
	    newbx *= linearInterpLength;
	    newcx *= linearInterpLength;
	    screenz = nearPlaneDepth;
	    firstSegment = false;
	  } else{
  	    accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate)
	  }
	}

    ytop*=SCREEN_WIDTH;
    ybottom*=SCREEN_WIDTH;
    int p = m_index;

    float iuf = iuadd;
    float ivf = ivadd;
    float irf = iradd;
    float igf = igadd;
    float ibf = ibadd;

    while (ytop < ybottom) {
      int xstart = (int) (xleft + PIXEL_CENTER);
      if (xstart < 0)
        xstart = 0;

	  int xpixel = xstart;//accurate mode

      int xend = (int) (xrght + PIXEL_CENTER);
      if (xend > SCREEN_WIDTH)
        xend = SCREEN_WIDTH;
      float xdiff = (xstart + PIXEL_CENTER) - xleft;

      int iu = (int) (iuf * xdiff + uleft);
      int iv = (int) (ivf * xdiff + vleft);
      int ir = (int) (irf * xdiff + rleft);
      int ig = (int) (igf * xdiff + gleft);
      int ib = (int) (ibf * xdiff + bleft);
      float iz = izadd * xdiff + zleft;

      xstart+=ytop;
      xend+=ytop;

      
	  if (accurateMode){
        screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); 
        screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f));
	    a = screenx*ax+screeny*ay+screenz*az;
	    b = screenx*bx+screeny*by+screenz*bz;
	    c = screenx*cx+screeny*cy+screenz*cz;
	  }
	  boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true;
	  int interpCounter = 0;
	  int deltaU = 0; int deltaV = 0;
	  float fu = 0; float fv = 0;
	  float oldfu = 0; float oldfv = 0;
	  
	  if (accurateMode&&goingIn){
	  	int rightOffset = (xend-xstart-1)%linearInterpLength;
		int leftOffset = linearInterpLength-rightOffset;
        float rightOffset2 = rightOffset / ((float)linearInterpLength);
		float leftOffset2 = leftOffset / ((float)linearInterpLength);
		interpCounter = leftOffset;
    	float ao = a-leftOffset2*newax;
        float bo = b-leftOffset2*newbx;
		float co = c-leftOffset2*newcx;
		float oneoverc = 65536.0f/co;
		oldfu = (ao*oneoverc); oldfv = (bo*oneoverc);
		a += rightOffset2*newax;
		b += rightOffset2*newbx;
		c += rightOffset2*newcx;
		oneoverc = 65536.0f/c;
		fu = a*oneoverc; fv = b*oneoverc;
		deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
		deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
		iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack		
	  } else{
	    float preoneoverc = 65536.0f/c;
	    fu = (a*preoneoverc);
        fv = (b*preoneoverc);
	  }

      for ( ; xstart < xend; xstart++ ) {
		if(accurateMode){
			    if (interpCounter == linearInterpLength) interpCounter = 0;
				if (interpCounter == 0){
			        a += newax;
                    b += newbx;
                    c += newcx;
					float oneoverc = 65536.0f/c;
					oldfu = fu; oldfv = fv;
					fu = (a*oneoverc); fv = (b*oneoverc);
					iu = (int)oldfu; iv = (int)oldfv;
					deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
					deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
				} else{
				    iu += deltaU;
					iv += deltaV;
				}
				interpCounter++;
		}	

        try
        {
          if (noDepthTest || (iz <= m_zbuffer[xstart])) {
            m_zbuffer[xstart] = iz;

            int red;
            int grn;
            int blu;

            if (m_bilinear) {
              int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16);
              int iui = (iu & 0xFFFF) >> 9;
              int ivi = (iv & 0xFFFF) >> 9;

              // get texture pixels
              int pix0 = m_texture[ofs];
              int pix1 = m_texture[ofs + 1];
              if (ofs < lastRowStart) ofs+=TEX_WIDTH;
              int pix2 = m_texture[ofs];
              int pix3 = m_texture[ofs + 1];

              // red
              int red0 = (pix0 & 0xFF0000);
              int red2 = (pix2 & 0xFF0000);
              int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7);
              int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7);
              red = up + (((dn-up) * ivi) >> 7);

              // grn
              red0 = (pix0 & 0xFF00);
              red2 = (pix2 & 0xFF00);
              up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7);
              dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7);
              grn = up + (((dn-up) * ivi) >> 7);

              // blu
              red0 = (pix0 & 0xFF);
              red2 = (pix2 & 0xFF);
              up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7);
              dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7);
              blu = up + (((dn-up) * ivi) >> 7);
            } else {
              // get texture pixel color
              blu = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)];
              red = (blu & 0xFF0000);
              grn = (blu & 0xFF00);
              blu = blu & 0xFF;
            }

            //
            int r = (ir >> 16);
            int g = (ig >> 16);
            int bb2 = (ib >> 16); //oops, namespace collision with accurate texture vector b...sorry [ewjordan]

            //
            m_pixels[xstart] = ( ((red * r) & 0xFF000000) | ((grn * g) & 0xFF0000) | (blu * bb2) ) >> 8;
            m_stencil[xstart] = p;
          }
        }
        catch (Exception e) {
        }

        //
        xpixel++;//accurate mode
		if (!accurateMode){
        iu+=iuadd;
        iv+=ivadd;
		}
        ir+=iradd;
        ig+=igadd;
        ib+=ibadd;
        iz+=izadd;
      }
	  ypixel++;//accurate mode

      ytop+=SCREEN_WIDTH;
      xleft+=leftadd;
      xrght+=rghtadd;
      uleft+=uleftadd;
      vleft+=vleftadd;
      rleft+=rleftadd;
      gleft+=gleftadd;
      bleft+=bleftadd;
      zleft+=zleftadd;
    }
  }


  /**
   * Gouraud*texture blended with interpolating alpha
   */
  private void drawsegment_gouraud_texture24_alpha
  (
    float leftadd,
    float rghtadd,
    int ytop,
    int ybottom
  ) {

  //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details
  	int ypixel = ytop;
    int lastRowStart = m_texture.length - TEX_WIDTH - 2;
	boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES];
	float screenx = 0; float screeny = 0; float screenz = 0;
	float a = 0; float b = 0; float c = 0;
	int linearInterpPower = TEX_INTERP_POWER;
	int linearInterpLength = 1 << linearInterpPower;
	if (accurateMode){
	  if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup
	    newax *= linearInterpLength;
	    newbx *= linearInterpLength;
	    newcx *= linearInterpLength;
	    screenz = nearPlaneDepth;
	    firstSegment = false;
	  } else{
  	    accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate)
	  }
	}

    ytop*=SCREEN_WIDTH;
    ybottom*=SCREEN_WIDTH;
    int p = m_index;

    float iuf = iuadd;
    float ivf = ivadd;
    float irf = iradd;
    float igf = igadd;
    float ibf = ibadd;
    float iaf = iaadd;

    while (ytop < ybottom) {
      int xstart = (int) (xleft + PIXEL_CENTER);
      if (xstart < 0)
        xstart = 0;

	  int xpixel = xstart;//accurate mode
	
      int xend = (int) (xrght + PIXEL_CENTER);
      if (xend > SCREEN_WIDTH)
        xend = SCREEN_WIDTH;
      float xdiff = (xstart + PIXEL_CENTER) - xleft;

      int iu = (int) (iuf * xdiff + uleft);
      int iv = (int) (ivf * xdiff + vleft);
      int ir = (int) (irf * xdiff + rleft);
      int ig = (int) (igf * xdiff + gleft);
      int ib = (int) (ibf * xdiff + bleft);
      int ia = (int) (iaf * xdiff + aleft);
      float iz = izadd * xdiff + zleft;

      xstart+=ytop;
      xend+=ytop;

	  if (accurateMode){
        screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); 
        screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f));
	    a = screenx*ax+screeny*ay+screenz*az;
	    b = screenx*bx+screeny*by+screenz*bz;
	    c = screenx*cx+screeny*cy+screenz*cz;
	  }
	  boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true;
	  int interpCounter = 0;
	  int deltaU = 0; int deltaV = 0;
	  float fu = 0; float fv = 0;
	  float oldfu = 0; float oldfv = 0;
	  
	  if (accurateMode&&goingIn){
	  	int rightOffset = (xend-xstart-1)%linearInterpLength;
		int leftOffset = linearInterpLength-rightOffset;
        float rightOffset2 = rightOffset / ((float)linearInterpLength);
		float leftOffset2 = leftOffset / ((float)linearInterpLength);
		interpCounter = leftOffset;
    	float ao = a-leftOffset2*newax;
        float bo = b-leftOffset2*newbx;
		float co = c-leftOffset2*newcx;
		float oneoverc = 65536.0f/co;
		oldfu = (ao*oneoverc); oldfv = (bo*oneoverc);
		a += rightOffset2*newax;
		b += rightOffset2*newbx;
		c += rightOffset2*newcx;
		oneoverc = 65536.0f/c;
		fu = a*oneoverc; fv = b*oneoverc;
		deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
		deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
		iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack		
	  } else{
	    float preoneoverc = 65536.0f/c;
	    fu = (a*preoneoverc);
        fv = (b*preoneoverc);
	  }

      for ( ;xstart < xend; xstart++ ) {
		if(accurateMode){
			    if (interpCounter == linearInterpLength) interpCounter = 0;
				if (interpCounter == 0){
			        a += newax;
                    b += newbx;
                    c += newcx;
					float oneoverc = 65536.0f/c;
					oldfu = fu; oldfv = fv;
					fu = (a*oneoverc); fv = (b*oneoverc);
					iu = (int)oldfu; iv = (int)oldfv;
					deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
					deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
				} else{
				    iu += deltaU;
					iv += deltaV;
				}
				interpCounter++;
		}	

        // get texture pixel color
        try
        {
          //if (iz < m_zbuffer[xstart]) {
          if (noDepthTest || (iz <= m_zbuffer[xstart])) {  // [fry 041114]
            //m_zbuffer[xstart] = iz;

            // blend
            int al = ia >> 16;

            int red;
            int grn;
            int blu;

            if (m_bilinear) {
              int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16);
              int iui = (iu & 0xFFFF) >> 9;
              int ivi = (iv & 0xFFFF) >> 9;

              // get texture pixels
              int pix0 = m_texture[ofs];
              int pix1 = m_texture[ofs + 1];
              if (ofs < lastRowStart) ofs+=TEX_WIDTH;
              int pix2 = m_texture[ofs];
              int pix3 = m_texture[ofs + 1];

              // red
              int red0 = (pix0 & 0xFF0000);
              int red2 = (pix2 & 0xFF0000);
              int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7);
              int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7);
              red = (up + (((dn-up) * ivi) >> 7)) >> 16;

              // grn
              red0 = (pix0 & 0xFF00);
              red2 = (pix2 & 0xFF00);
              up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7);
              dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7);
              grn = (up + (((dn-up) * ivi) >> 7)) >> 8;

              // blu
              red0 = (pix0 & 0xFF);
              red2 = (pix2 & 0xFF);
              up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7);
              dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7);
              blu = up + (((dn-up) * ivi) >> 7);
            } else {
              blu = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)];
              red = (blu & 0xFF0000) >> 16; // 0 - 255
              grn = (blu & 0xFF00) >> 8;    // 0 - 255
              blu = (blu & 0xFF);       // 0 - 255
            }

            // multiply with gouraud color
            red = (red * ir) >>> 8;       // 0x00FF????
            grn = (grn * ig) >>> 16;      // 0x0000FF??
            blu = (blu * ib) >>> 24;      // 0x000000FF

            // get buffer pixels
            int bb = m_pixels[xstart];
            int br = (bb & 0xFF0000);     // 0x00FF0000
            int bg = (bb & 0xFF00);       // 0x0000FF00
            bb = (bb & 0xFF);         // 0x000000FF

            //
            m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF);
            m_stencil[xstart] = p;
          }
        }
        catch (Exception e) {
        }

        //
        xpixel++;//accurate mode
		if (!accurateMode){
        iu+=iuadd;
        iv+=ivadd;
		}
        ir+=iradd;
        ig+=igadd;
        ib+=ibadd;
        ia+=iaadd;
        iz+=izadd;
      }

	  ypixel++;//accurate mode
	  
      ytop+=SCREEN_WIDTH;
      xleft+=leftadd;
      xrght+=rghtadd;
      uleft+=uleftadd;
      vleft+=vleftadd;
      rleft+=rleftadd;
      gleft+=gleftadd;
      bleft+=bleftadd;
      aleft+=aleftadd;
      zleft+=zleftadd;
    }
  }


  /**
   * Gouraud*texture blended with interpolating alpha
   */
  private void drawsegment_gouraud_texture32
  (
    float leftadd,
    float rghtadd,
    int ytop,
    int ybottom
  ) {
  
    //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details
  	int ypixel = ytop;
    int lastRowStart = m_texture.length - TEX_WIDTH - 2;
	boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES];
	float screenx = 0; float screeny = 0; float screenz = 0;
	float a = 0; float b = 0; float c = 0;
	int linearInterpPower = TEX_INTERP_POWER;
	int linearInterpLength = 1 << linearInterpPower;
	if (accurateMode){
	  if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup
	    newax *= linearInterpLength;
	    newbx *= linearInterpLength;
	    newcx *= linearInterpLength;
	    screenz = nearPlaneDepth;
	    firstSegment = false;
	  } else{
  	    accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate)
	  }
	}

    ytop*=SCREEN_WIDTH;
    ybottom*=SCREEN_WIDTH;
    //int p = m_index;

    float iuf = iuadd;
    float ivf = ivadd;
    float irf = iradd;
    float igf = igadd;
    float ibf = ibadd;

    while (ytop < ybottom) {
      int xstart = (int) (xleft + PIXEL_CENTER);
      if (xstart < 0)
        xstart = 0;
    
	  int xpixel = xstart;//accurate mode

      int xend = (int) (xrght + PIXEL_CENTER);
      if (xend > SCREEN_WIDTH)
        xend = SCREEN_WIDTH;
      float xdiff = (xstart + PIXEL_CENTER) - xleft;

      int iu = (int) (iuf * xdiff + uleft);
      int iv = (int) (ivf * xdiff + vleft);
      int ir = (int) (irf * xdiff + rleft);
      int ig = (int) (igf * xdiff + gleft);
      int ib = (int) (ibf * xdiff + bleft);
      float iz = izadd * xdiff + zleft;

      xstart+=ytop;
      xend+=ytop;
	  if (accurateMode){
        screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); 
        screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f));
	    a = screenx*ax+screeny*ay+screenz*az;
	    b = screenx*bx+screeny*by+screenz*bz;
	    c = screenx*cx+screeny*cy+screenz*cz;
	  }
	  boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true;
	  int interpCounter = 0;
	  int deltaU = 0; int deltaV = 0;
	  float fu = 0; float fv = 0;
	  float oldfu = 0; float oldfv = 0;
	  
	  if (accurateMode&&goingIn){
	  	int rightOffset = (xend-xstart-1)%linearInterpLength;
		int leftOffset = linearInterpLength-rightOffset;
        float rightOffset2 = rightOffset / ((float)linearInterpLength);
		float leftOffset2 = leftOffset / ((float)linearInterpLength);
		interpCounter = leftOffset;
    	float ao = a-leftOffset2*newax;
        float bo = b-leftOffset2*newbx;
		float co = c-leftOffset2*newcx;
		float oneoverc = 65536.0f/co;
		oldfu = (ao*oneoverc); oldfv = (bo*oneoverc);
		a += rightOffset2*newax;
		b += rightOffset2*newbx;
		c += rightOffset2*newcx;
		oneoverc = 65536.0f/c;
		fu = a*oneoverc; fv = b*oneoverc;
		deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
		deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
		iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack		
	  } else{
	    float preoneoverc = 65536.0f/c;
	    fu = (a*preoneoverc);
        fv = (b*preoneoverc);
	  }
	  

      for ( ; xstart < xend; xstart++ ) {
		if(accurateMode){
			    if (interpCounter == linearInterpLength) interpCounter = 0;
				if (interpCounter == 0){
			        a += newax;
                    b += newbx;
                    c += newcx;
					float oneoverc = 65536.0f/c;
					oldfu = fu; oldfv = fv;
					fu = (a*oneoverc); fv = (b*oneoverc);
					iu = (int)oldfu; iv = (int)oldfv;
					deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
					deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
				} else{
				    iu += deltaU;
					iv += deltaV;
				}
				interpCounter++;
		}	

        try
        {
          if (noDepthTest || (iz <= m_zbuffer[xstart])) {
            //m_zbuffer[xstart] = iz;

            int red;
            int grn;
            int blu;
            int al;

            if (m_bilinear) {
              int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16);
              int iui = (iu & 0xFFFF) >> 9;
              int ivi = (iv & 0xFFFF) >> 9;

              // get texture pixels
              int pix0 = m_texture[ofs];
              int pix1 = m_texture[ofs + 1];
              if (ofs < lastRowStart) ofs+=TEX_WIDTH;
              int pix2 = m_texture[ofs];
              int pix3 = m_texture[ofs + 1];

              // red
              int red0 = (pix0 & 0xFF0000);
              int red2 = (pix2 & 0xFF0000);
              int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7);
              int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7);
              red = (up + (((dn-up) * ivi) >> 7)) >> 16;

              // grn
              red0 = (pix0 & 0xFF00);
              red2 = (pix2 & 0xFF00);
              up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7);
              dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7);
              grn = (up + (((dn-up) * ivi) >> 7)) >> 8;

              // blu
              red0 = (pix0 & 0xFF);
              red2 = (pix2 & 0xFF);
              up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7);
              dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7);
              blu = up + (((dn-up) * ivi) >> 7);

              // alpha
              pix0>>>=24;
              pix2>>>=24;
              up = pix0 + ((((pix1 >>> 24) - pix0) * iui) >> 7);
              dn = pix2 + ((((pix3 >>> 24) - pix2) * iui) >> 7);
              al = up + (((dn-up) * ivi) >> 7);
            } else {
              // get texture pixel color
              blu = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)];
              al = (blu >>> 24);
              red = (blu & 0xFF0000) >> 16;
              grn = (blu & 0xFF00) >> 8;
              blu = blu & 0xFF;
            }

            // multiply with gouraud color
            red = (red * ir) >>> 8;       // 0x00FF????
            grn = (grn * ig) >>> 16;      // 0x0000FF??
            blu = (blu * ib) >>> 24;      // 0x000000FF

            // get buffer pixels
            int bb = m_pixels[xstart];
            int br = (bb & 0xFF0000);     // 0x00FF0000
            int bg = (bb & 0xFF00);       // 0x0000FF00
            bb = (bb & 0xFF);         // 0x000000FF

            //
            m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF);
          }
        }
        catch (Exception e) {
        }

        //
		xpixel++;//accurate mode
		if (!accurateMode){
        iu+=iuadd;
        iv+=ivadd;
		}
        ir+=iradd;
        ig+=igadd;
        ib+=ibadd;
        iz+=izadd;
      }
	  ypixel++;//accurate mode
      ytop+=SCREEN_WIDTH;
      xleft+=leftadd;
      xrght+=rghtadd;
      uleft+=uleftadd;
      vleft+=vleftadd;
      rleft+=rleftadd;
      gleft+=gleftadd;
      bleft+=bleftadd;
      zleft+=zleftadd;
    }
  }


  /**
   * Gouraud*texture blended with interpolating alpha
   */
  private void drawsegment_gouraud_texture32_alpha
  (
    float leftadd,
    float rghtadd,
    int ytop,
    int ybottom
  ) {
  //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details
  	int ypixel = ytop;
    int lastRowStart = m_texture.length - TEX_WIDTH - 2;
	boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES];
	float screenx = 0; float screeny = 0; float screenz = 0;
	float a = 0; float b = 0; float c = 0;
	int linearInterpPower = TEX_INTERP_POWER;
	int linearInterpLength = 1 << linearInterpPower;
	if (accurateMode){
	  if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup
	    newax *= linearInterpLength;
	    newbx *= linearInterpLength;
	    newcx *= linearInterpLength;
	    screenz = nearPlaneDepth;
	    firstSegment = false;
	  } else{
  	    accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate)
	  }
	}

    ytop*=SCREEN_WIDTH;
    ybottom*=SCREEN_WIDTH;
    int p = m_index;

    float iuf = iuadd;
    float ivf = ivadd;
    float irf = iradd;
    float igf = igadd;
    float ibf = ibadd;
    float iaf = iaadd;

    while (ytop < ybottom) {
      int xstart = (int) (xleft + PIXEL_CENTER);
      if (xstart < 0)
        xstart = 0;

	  int xpixel = xstart;//accurate mode

      int xend = (int) (xrght + PIXEL_CENTER);
      if (xend > SCREEN_WIDTH)
        xend = SCREEN_WIDTH;
      float xdiff = (xstart + PIXEL_CENTER) - xleft;

      int iu = (int) (iuf * xdiff + uleft);
      int iv = (int) (ivf * xdiff + vleft);
      int ir = (int) (irf * xdiff + rleft);
      int ig = (int) (igf * xdiff + gleft);
      int ib = (int) (ibf * xdiff + bleft);
      int ia = (int) (iaf * xdiff + aleft);
      float iz = izadd * xdiff + zleft;

      xstart+=ytop;
      xend+=ytop;
	  if (accurateMode){
        screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); 
        screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f));
	    a = screenx*ax+screeny*ay+screenz*az;
	    b = screenx*bx+screeny*by+screenz*bz;
	    c = screenx*cx+screeny*cy+screenz*cz;
	  }
	  boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true;
	  int interpCounter = 0;
	  int deltaU = 0; int deltaV = 0;
	  float fu = 0; float fv = 0;
	  float oldfu = 0; float oldfv = 0;
	  
	  if (accurateMode&&goingIn){
	  	int rightOffset = (xend-xstart-1)%linearInterpLength;
		int leftOffset = linearInterpLength-rightOffset;
        float rightOffset2 = rightOffset / ((float)linearInterpLength);
		float leftOffset2 = leftOffset / ((float)linearInterpLength);
		interpCounter = leftOffset;
    	float ao = a-leftOffset2*newax;
        float bo = b-leftOffset2*newbx;
		float co = c-leftOffset2*newcx;
		float oneoverc = 65536.0f/co;
		oldfu = (ao*oneoverc); oldfv = (bo*oneoverc);
		a += rightOffset2*newax;
		b += rightOffset2*newbx;
		c += rightOffset2*newcx;
		oneoverc = 65536.0f/c;
		fu = a*oneoverc; fv = b*oneoverc;
		deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
		deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
		iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack		
	  } else{
	    float preoneoverc = 65536.0f/c;
	    fu = (a*preoneoverc);
        fv = (b*preoneoverc);
	  }

      for ( ;xstart < xend; xstart++ ) {
		if(accurateMode){
			    if (interpCounter == linearInterpLength) interpCounter = 0;
				if (interpCounter == 0){
			        a += newax;
                    b += newbx;
                    c += newcx;
					float oneoverc = 65536.0f/c;
					oldfu = fu; oldfv = fv;
					fu = (a*oneoverc); fv = (b*oneoverc);
					iu = (int)oldfu; iv = (int)oldfv;
					deltaU = ((int)(fu - oldfu)) >> linearInterpPower;
					deltaV = ((int)(fv - oldfv)) >> linearInterpPower;
				} else{
				    iu += deltaU;
					iv += deltaV;
				}
				interpCounter++;
		}	

        // get texture pixel color
        try
        {
          //if (iz < m_zbuffer[xstart]) {
          if (noDepthTest || (iz <= m_zbuffer[xstart])) {  // [fry 041114]
            //m_zbuffer[xstart] = iz;

            // blend
            int al = ia >> 16;

            int red;
            int grn;
            int blu;

            if (m_bilinear) {
              int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16);
              int iui = (iu & 0xFFFF) >> 9;
              int ivi = (iv & 0xFFFF) >> 9;

              // get texture pixels
              int pix0 = m_texture[ofs];
              int pix1 = m_texture[ofs + 1];
              if (ofs < lastRowStart) ofs+=TEX_WIDTH;
              int pix2 = m_texture[ofs];
              int pix3 = m_texture[ofs + 1];

              // red
              int red0 = (pix0 & 0xFF0000);
              int red2 = (pix2 & 0xFF0000);
              int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7);
              int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7);
              red = (up + (((dn-up) * ivi) >> 7)) >> 16;

              // grn
              red0 = (pix0 & 0xFF00);
              red2 = (pix2 & 0xFF00);
              up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7);
              dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7);
              grn = (up + (((dn-up) * ivi) >> 7)) >> 8;

              // blu
              red0 = (pix0 & 0xFF);
              red2 = (pix2 & 0xFF);
              up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7);
              dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7);
              blu = up + (((dn-up) * ivi) >> 7);

              // alpha
              pix0>>>=24;
              pix2>>>=24;
              up = pix0 + ((((pix1 >>> 24) - pix0) * iui) >> 7);
              dn = pix2 + ((((pix3 >>> 24) - pix2) * iui) >> 7);
              al = al * (up + (((dn-up) * ivi) >> 7)) >> 8;
            } else {
              blu = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)];
              al = al * (blu >>> 24) >> 8;
              red = (blu & 0xFF0000) >> 16; // 0 - 255
              grn = (blu & 0xFF00) >> 8;    // 0 - 255
              blu = (blu & 0xFF);       // 0 - 255
            }

            // multiply with gouraud color
            red = (red * ir) >>> 8;       // 0x00FF????
            grn = (grn * ig) >>> 16;      // 0x0000FF??
            blu = (blu * ib) >>> 24;      // 0x000000FF

            // get buffer pixels
            int bb = m_pixels[xstart];
            int br = (bb & 0xFF0000);     // 0x00FF0000
            int bg = (bb & 0xFF00);       // 0x0000FF00
            bb = (bb & 0xFF);         // 0x000000FF

            //
            m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF);
            m_stencil[xstart] = p;
          }
        }
        catch (Exception e) {
        }

        //
        xpixel++;//accurate mode
		if (!accurateMode){
        iu+=iuadd;
        iv+=ivadd;
		}
        ir+=iradd;
        ig+=igadd;
        ib+=ibadd;
        ia+=iaadd;
        iz+=izadd;
      }
	  ypixel++;//accurate mode
      ytop+=SCREEN_WIDTH;
      xleft+=leftadd;
      xrght+=rghtadd;
      uleft+=uleftadd;
      vleft+=vleftadd;
      rleft+=rleftadd;
      gleft+=gleftadd;
      bleft+=bleftadd;
      aleft+=aleftadd;
      zleft+=zleftadd;
    }
  }
}
